Compare commits
80
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3df1cfcaf2 | ||
|
|
62bf420c1b | ||
|
|
85a80750e2 | ||
|
|
148a9b85d2 | ||
|
|
ece6b456c2 | ||
|
|
f214a45a35 | ||
|
|
355954ffca | ||
|
|
18177a1a60 | ||
|
|
efeb75b805 | ||
|
|
bb154ac50c | ||
|
|
fb6cccb863 | ||
|
|
38bba9cdd5 | ||
|
|
f1a3d03741 | ||
|
|
90c833870c | ||
|
|
049e14f0e3 | ||
|
|
20fa828984 | ||
|
|
5e06963394 | ||
|
|
204ebc25e6 | ||
|
|
043f9f51f9 | ||
|
|
cd40f6564e | ||
|
|
70bc3ead4f | ||
|
|
7291ea0bff | ||
|
|
e4aaa45805 | ||
|
|
011cc0be51 | ||
|
|
af497b5672 | ||
|
|
49c5202522 | ||
|
|
c3e607496c | ||
|
|
439aa1d6e9 | ||
|
|
3d495c61ef | ||
|
|
d5477baf7d | ||
|
|
c852036b4a | ||
|
|
0c55a384f8 | ||
|
|
0946d1209d | ||
|
|
edcb3ac59a | ||
|
|
9e03364999 | ||
|
|
f9f8a5312e | ||
|
|
36434220fc | ||
|
|
ebba39e4e1 | ||
|
|
bbad4c6989 | ||
|
|
1bf4098e03 | ||
|
|
7b5a1e7d51 | ||
|
|
a9f1fc6c94 | ||
|
|
7b685f5229 | ||
|
|
1affde2f10 | ||
|
|
f7c93b3cee | ||
|
|
66bcfbb130 | ||
|
|
d812e6d76e | ||
|
|
ebab096e86 | ||
|
|
9ad36ad57f | ||
|
|
9931f817b7 | ||
|
|
9208f57b16 | ||
|
|
403d309857 | ||
|
|
ca5e1cdf8e | ||
|
|
e93ccb3290 | ||
|
|
538531cde5 | ||
|
|
fe24139702 | ||
|
|
9aa219a1fe | ||
|
|
86578bb04c | ||
|
|
5620033115 | ||
|
|
473808da0d | ||
|
|
caf3746678 | ||
|
|
6293eb04df | ||
|
|
08b59d10e5 | ||
|
|
20451195f0 | ||
|
|
699541c4b3 | ||
|
|
e80d6c689b | ||
|
|
d541938c48 | ||
|
|
1e2631d6f8 | ||
|
|
f6da8b2200 | ||
|
|
1c986f42ff | ||
|
|
3ae2e86baf | ||
|
|
466aa57a45 | ||
|
|
ef2dcdccaa | ||
|
|
5d63ca6c38 | ||
|
|
4e10acb3e5 | ||
|
|
3b3619a327 | ||
|
|
ac99217e92 | ||
|
|
0a375f5abd | ||
|
|
e8db8b845a | ||
|
|
29c36e9f36 |
No files matched your search
+20
-10
@@ -65,7 +65,8 @@ Awesome! Please provide the following information:
|
||||
If you are willing to contribute the model yourself, let us know so we can best
|
||||
guide you.
|
||||
|
||||
We have added a **detailed guide and templates** to guide you in the process of adding a new model. You can find them in the [`templates`](./templates) folder.
|
||||
We have added a **detailed guide and templates** to guide you in the process of adding a new model. You can find them
|
||||
in the [`templates`](https://github.com/huggingface/transformers/templates) folder.
|
||||
|
||||
### Do you want a new feature (that is not a model)?
|
||||
|
||||
@@ -86,7 +87,9 @@ A world-class feature request addresses the following points:
|
||||
If your issue is well written we're already 80% of the way there by the time you
|
||||
post it.
|
||||
|
||||
We have added **templates** to guide you in the process of adding a new example script for training or testing the models in the library. You can find them in the [`templates`](./templates) folder.
|
||||
We have added **templates** to guide you in the process of adding a new example script for training or testing the
|
||||
models in the library. You can find them in the [`templates`](https://github.com/huggingface/transformers/templates)
|
||||
folder.
|
||||
|
||||
## Start contributing! (Pull Requests)
|
||||
|
||||
@@ -206,15 +209,21 @@ Follow these steps to start contributing:
|
||||
to be merged;
|
||||
4. Make sure existing tests pass;
|
||||
5. Add high-coverage tests. No quality testing = no merge.
|
||||
- If you are adding a new model, make sure that you use `ModelTester.all_model_classes = (MyModel, MyModelWithLMHead,...)`, which triggers the common tests.
|
||||
- If you are adding new `@slow` tests, make sure they pass using `RUN_SLOW=1 python -m pytest tests/test_my_new_model.py`.
|
||||
- If you are adding a new tokenizer, write tests, and make sure `RUN_SLOW=1 python -m pytest tests/test_tokenization_{your_model_name}.py` passes.
|
||||
CircleCI does not run them.
|
||||
6. All public methods must have informative docstrings that work nicely with sphinx. See `modeling_ctrl.py` for an example.
|
||||
- If you are adding a new model, make sure that you use
|
||||
`ModelTester.all_model_classes = (MyModel, MyModelWithLMHead,...)`, which triggers the common tests.
|
||||
- If you are adding new `@slow` tests, make sure they pass using
|
||||
`RUN_SLOW=1 python -m pytest tests/test_my_new_model.py`.
|
||||
- If you are adding a new tokenizer, write tests, and make sure
|
||||
`RUN_SLOW=1 python -m pytest tests/test_tokenization_{your_model_name}.py` passes.
|
||||
CircleCI does not run the slow tests.
|
||||
6. All public methods must have informative docstrings that work nicely with sphinx. See `modeling_ctrl.py` for an
|
||||
example.
|
||||
|
||||
### Tests
|
||||
|
||||
You can run 🤗 Transformers tests with `unittest` or `pytest`.
|
||||
An extensive test suite is included to test the library behavior and several examples. Library tests can be found in
|
||||
the [tests folder](https://github.com/huggingface/transformers/tree/master/tests) and examples tests in the
|
||||
[examples folder](https://github.com/huggingface/transformers/tree/master/examples).
|
||||
|
||||
We like `pytest` and `pytest-xdist` because it's faster. From the root of the
|
||||
repository, here's how to run tests with `pytest` for the library:
|
||||
@@ -261,7 +270,8 @@ $ python -m unittest discover -s examples -t examples -v
|
||||
|
||||
### Style guide
|
||||
|
||||
For documentation strings, `transformers` follows the [google
|
||||
style](https://google.github.io/styleguide/pyguide.html).
|
||||
For documentation strings, `transformers` follows the [google style](https://google.github.io/styleguide/pyguide.html).
|
||||
Check our [documentation writing guide](https://github.com/huggingface/transformers/tree/master/docs#writing-documentation---specification)
|
||||
for more information.
|
||||
|
||||
#### This guide was heavily inspired by the awesome [scikit-learn guide to contributing](https://github.com/scikit-learn/scikit-learn/blob/master/CONTRIBUTING.md)
|
||||
@@ -538,20 +538,21 @@ You can create `Pipeline` objects for the following down-stream tasks:
|
||||
- `translation_xx_to_yy`
|
||||
|
||||
```python
|
||||
from transformers import pipeline
|
||||
>>> from transformers import pipeline
|
||||
|
||||
# Allocate a pipeline for sentiment-analysis
|
||||
nlp = pipeline('sentiment-analysis')
|
||||
nlp('We are very happy to include pipeline into the transformers repository.')
|
||||
>>> {'label': 'POSITIVE', 'score': 0.99893874}
|
||||
>>> nlp = pipeline('sentiment-analysis')
|
||||
>>> nlp('We are very happy to include pipeline into the transformers repository.')
|
||||
[{'label': 'POSITIVE', 'score': 0.9978193640708923}]
|
||||
|
||||
# Allocate a pipeline for question-answering
|
||||
nlp = pipeline('question-answering')
|
||||
nlp({
|
||||
'question': 'What is the name of the repository ?',
|
||||
'context': 'Pipeline have been included in the huggingface/transformers repository'
|
||||
})
|
||||
>>> {'score': 0.28756016668193496, 'start': 35, 'end': 59, 'answer': 'huggingface/transformers'}
|
||||
>>> nlp = pipeline('question-answering')
|
||||
>>> nlp({
|
||||
... 'question': 'What is the name of the repository ?',
|
||||
... 'context': 'Pipeline have been included in the huggingface/transformers repository'
|
||||
... })
|
||||
{'score': 0.5135612454720828, 'start': 35, 'end': 59, 'answer': 'huggingface/transformers'}
|
||||
|
||||
```
|
||||
|
||||
## Migrating from pytorch-transformers to transformers
|
||||
|
||||
+6
-12
@@ -42,20 +42,14 @@ pip install recommonmark
|
||||
|
||||
## Building the documentation
|
||||
|
||||
Make sure that there is a symlink from the `example` file (in /examples) inside the source folder. Run the following
|
||||
command to generate it:
|
||||
|
||||
```bash
|
||||
ln -s ../../examples/README.md examples.md
|
||||
```
|
||||
|
||||
Once you have setup `sphinx`, you can build the documentation by running the following command in the `/docs` folder:
|
||||
|
||||
```bash
|
||||
make html
|
||||
```
|
||||
|
||||
A folder called ``_build/html`` should have been created. You can now open the file ``_build/html/index.html`` in your browser.
|
||||
A folder called ``_build/html`` should have been created. You can now open the file ``_build/html/index.html`` in your
|
||||
browser.
|
||||
|
||||
---
|
||||
**NOTE**
|
||||
@@ -132,8 +126,8 @@ XXXConfig
|
||||
:members:
|
||||
```
|
||||
|
||||
This will include every public method of the configuration. If for some reason you wish for a method not to be displayed
|
||||
in the documentation, you can do so by specifying which methods should be in the docs:
|
||||
This will include every public method of the configuration. If for some reason you wish for a method not to be
|
||||
displayed in the documentation, you can do so by specifying which methods should be in the docs:
|
||||
|
||||
```
|
||||
XXXTokenizer
|
||||
@@ -147,8 +141,8 @@ XXXTokenizer
|
||||
|
||||
### Writing source documentation
|
||||
|
||||
Values that should be put in `code` should either be surrounded by double backticks: \`\`like so\`\` or be written as an object
|
||||
using the :obj: syntax: :obj:\`like so\`.
|
||||
Values that should be put in `code` should either be surrounded by double backticks: \`\`like so\`\` or be written as
|
||||
an object using the :obj: syntax: :obj:\`like so\`.
|
||||
|
||||
When mentionning a class, it is recommended to use the :class: syntax as the mentioned class will be automatically
|
||||
linked by Sphinx: :class:\`transformers.XXXClass\`
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../CONTRIBUTING.md
|
||||
+105
-32
@@ -1,17 +1,18 @@
|
||||
Transformers
|
||||
================================================================================================================================================
|
||||
|
||||
🤗 Transformers (formerly known as `pytorch-transformers` and `pytorch-pretrained-bert`) provides general-purpose architectures
|
||||
(BERT, GPT-2, RoBERTa, XLM, DistilBert, XLNet...) for Natural Language Understanding (NLU) and Natural Language Generation
|
||||
(NLG) with over 32+ pretrained models in 100+ languages and deep interoperability between TensorFlow 2.0 and PyTorch.
|
||||
State-of-the-art Natural Language Processing for Pytorch and TensorFlow 2.0.
|
||||
|
||||
This is the documentation of our repository `transformers <https://github.com/huggingface/transformers>`__.
|
||||
🤗 Transformers (formerly known as `pytorch-transformers` and `pytorch-pretrained-bert`) provides general-purpose
|
||||
architectures (BERT, GPT-2, RoBERTa, XLM, DistilBert, XLNet...) for Natural Language Understanding (NLU) and Natural
|
||||
Language Generation (NLG) with over 32+ pretrained models in 100+ languages and deep interoperability between
|
||||
TensorFlow 2.0 and PyTorch.
|
||||
|
||||
This is the documentation of our repository `transformers <https://github.com/huggingface/transformers>`_.
|
||||
|
||||
Features
|
||||
---------------------------------------------------
|
||||
|
||||
- As easy to use as pytorch-transformers
|
||||
- As powerful and concise as Keras
|
||||
- High performance on NLU and NLG tasks
|
||||
- Low barrier to entry for educators and practitioners
|
||||
|
||||
@@ -37,46 +38,122 @@ Choose the right framework for every part of a model's lifetime:
|
||||
Contents
|
||||
---------------------------------
|
||||
|
||||
The library currently contains PyTorch and Tensorflow implementations, pre-trained model weights, usage scripts and conversion utilities for the following models:
|
||||
The library currently contains PyTorch and Tensorflow implementations, pre-trained model weights, usage scripts and
|
||||
conversion utilities for the following models:
|
||||
|
||||
1. `BERT <https://github.com/google-research/bert>`_ (from Google) released with the paper `BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding <https://arxiv.org/abs/1810.04805>`_ by Jacob Devlin, Ming-Wei Chang, Kenton Lee and Kristina Toutanova.
|
||||
2. `GPT <https://github.com/openai/finetune-transformer-lm>`_ (from OpenAI) released with the paper `Improving Language Understanding by Generative Pre-Training <https://blog.openai.com/language-unsupervised>`_ by Alec Radford, Karthik Narasimhan, Tim Salimans and Ilya Sutskever.
|
||||
3. `GPT-2 <https://blog.openai.com/better-language-models>`_ (from OpenAI) released with the paper `Language Models are Unsupervised Multitask Learners <https://blog.openai.com/better-language-models>`_ by Alec Radford*, Jeffrey Wu*, Rewon Child, David Luan, Dario Amodei** and Ilya Sutskever**.
|
||||
4. `Transformer-XL <https://github.com/kimiyoung/transformer-xl>`_ (from Google/CMU) released with the paper `Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context <https://arxiv.org/abs/1901.02860>`_ by Zihang Dai*, Zhilin Yang*, Yiming Yang, Jaime Carbonell, Quoc V. Le, Ruslan Salakhutdinov.
|
||||
5. `XLNet <https://github.com/zihangdai/xlnet>`_ (from Google/CMU) released with the paper `XLNet: Generalized Autoregressive Pretraining for Language Understanding <https://arxiv.org/abs/1906.08237>`_ by Zhilin Yang*, Zihang Dai*, Yiming Yang, Jaime Carbonell, Ruslan Salakhutdinov, Quoc V. Le.
|
||||
6. `XLM <https://github.com/facebookresearch/XLM>`_ (from Facebook) released together with the paper `Cross-lingual Language Model Pretraining <https://arxiv.org/abs/1901.07291>`_ by Guillaume Lample and Alexis Conneau.
|
||||
7. `RoBERTa <https://github.com/pytorch/fairseq/tree/master/examples/roberta>`_ (from Facebook), released together with the paper a `Robustly Optimized BERT Pretraining Approach <https://arxiv.org/abs/1907.11692>`_ by Yinhan Liu, Myle Ott, Naman Goyal, Jingfei Du, Mandar Joshi, Danqi Chen, Omer Levy, Mike Lewis, Luke Zettlemoyer, Veselin Stoyanov.
|
||||
8. `DistilBERT <https://huggingface.co/transformers/model_doc/distilbert.html>`_ (from HuggingFace) released together with the paper `DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter <https://arxiv.org/abs/1910.01108>`_ by Victor Sanh, Lysandre Debut and Thomas Wolf. The same method has been applied to compress GPT2 into `DistilGPT2 <https://github.com/huggingface/transformers/tree/master/examples/distillation>`_.
|
||||
9. `CTRL <https://github.com/pytorch/fairseq/tree/master/examples/ctrl>`_ (from Salesforce), released together with the paper `CTRL: A Conditional Transformer Language Model for Controllable Generation <https://www.github.com/salesforce/ctrl>`_ by Nitish Shirish Keskar*, Bryan McCann*, Lav R. Varshney, Caiming Xiong and Richard Socher.
|
||||
10. `CamemBERT <https://huggingface.co/transformers/model_doc/camembert.html>`_ (from FAIR, Inria, Sorbonne Université) released together with the paper `CamemBERT: a Tasty French Language Model <https://arxiv.org/abs/1911.03894>`_ by Louis Martin, Benjamin Muller, Pedro Javier Ortiz Suarez, Yoann Dupont, Laurent Romary, Eric Villemonte de la Clergerie, Djame Seddah, and Benoît Sagot.
|
||||
11. `ALBERT <https://github.com/google-research/ALBERT>`_ (from Google Research), released together with the paper a `ALBERT: A Lite BERT for Self-supervised Learning of Language Representations <https://arxiv.org/abs/1909.11942>`_ by Zhenzhong Lan, Mingda Chen, Sebastian Goodman, Kevin Gimpel, Piyush Sharma, Radu Soricut.
|
||||
12. `XLM-RoBERTa <https://github.com/pytorch/fairseq/tree/master/examples/xlmr>`_ (from Facebook AI), released together with the paper `Unsupervised Cross-lingual Representation Learning at Scale <https://arxiv.org/abs/1911.02116>`_ by Alexis Conneau*, Kartikay Khandelwal*, Naman Goyal, Vishrav Chaudhary, Guillaume Wenzek, Francisco Guzmán, Edouard Grave, Myle Ott, Luke Zettlemoyer and Veselin Stoyanov.
|
||||
13. `FlauBERT <https://github.com/getalp/Flaubert>`_ (from CNRS) released with the paper `FlauBERT: Unsupervised Language Model Pre-training for French <https://arxiv.org/abs/1912.05372>`_ by Hang Le, Loïc Vial, Jibril Frej, Vincent Segonne, Maximin Coavoux, Benjamin Lecouteux, Alexandre Allauzen, Benoît Crabbé, Laurent Besacier, Didier Schwab.
|
||||
1. `BERT <https://github.com/google-research/bert>`_ (from Google) released with the paper `BERT: Pre-training of Deep
|
||||
Bidirectional Transformers for Language Understanding <https://arxiv.org/abs/1810.04805>`_ by Jacob Devlin, Ming-Wei
|
||||
Chang, Kenton Lee, and Kristina Toutanova.
|
||||
2. `GPT <https://github.com/openai/finetune-transformer-lm>`_ (from OpenAI) released with the paper `Improving Language
|
||||
Understanding by Generative Pre-Training <https://blog.openai.com/language-unsupervised>`_ by Alec Radford, Karthik
|
||||
Narasimhan, Tim Salimans, and Ilya Sutskever.
|
||||
3. `GPT-2 <https://blog.openai.com/better-language-models>`_ (from OpenAI) released with the paper `Language Models are
|
||||
Unsupervised Multitask Learners <https://blog.openai.com/better-language-models>`_ by Alec Radford, Jeffrey Wu,
|
||||
Rewon Child, David Luan, Dario Amodei, and Ilya Sutskever.
|
||||
4. `Transformer-XL <https://github.com/kimiyoung/transformer-xl>`_ (from Google/CMU) released with the paper
|
||||
`Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context <https://arxiv.org/abs/1901.02860>`_ by
|
||||
Zihang Dai, Zhilin Yang, Yiming Yang, Jaime Carbonell, Quoc V. Le, and Ruslan Salakhutdinov.
|
||||
5. `XLNet <https://github.com/zihangdai/xlnet>`_ (from Google/CMU) released with the paper `XLNet: Generalized
|
||||
Autoregressive Pretraining for Language Understanding <https://arxiv.org/abs/1906.08237>`_ by Zhilin Yang, Zihang
|
||||
Dai, Yiming Yang, Jaime Carbonell, Ruslan Salakhutdinov, and Quoc V. Le.
|
||||
6. `XLM <https://github.com/facebookresearch/XLM>`_ (from Facebook) released together with the paper `Cross-lingual
|
||||
Language Model Pretraining <https://arxiv.org/abs/1901.07291>`_ by Guillaume Lample and Alexis Conneau.
|
||||
7. `RoBERTa <https://github.com/pytorch/fairseq/tree/master/examples/roberta>`_ (from Facebook), released together with
|
||||
the paper a `Robustly Optimized BERT Pretraining Approach <https://arxiv.org/abs/1907.11692>`_ by Yinhan Liu, Myle
|
||||
Ott, Naman Goyal, Jingfei Du, Mandar Joshi, Danqi Chen, Omer Levy, Mike Lewis, Luke Zettlemoyer, and Veselin
|
||||
Stoyanov.
|
||||
8. `DistilBERT <https://huggingface.co/transformers/model_doc/distilbert.html>`_ (from HuggingFace) released together
|
||||
with the paper `DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter
|
||||
<https://arxiv.org/abs/1910.01108>`_ by Victor Sanh, Lysandre Debut, and Thomas Wolf. The same method has been
|
||||
applied to compress GPT2 into
|
||||
`DistilGPT2 <https://github.com/huggingface/transformers/tree/master/examples/distillation>`_.
|
||||
9. `CTRL <https://github.com/pytorch/fairseq/tree/master/examples/ctrl>`_ (from Salesforce), released together with the
|
||||
paper `CTRL: A Conditional Transformer Language Model for Controllable Generation
|
||||
<https://www.github.com/salesforce/ctrl>`_ by Nitish Shirish Keskar, Bryan McCann, Lav R. Varshney, Caiming Xiong,
|
||||
and Richard Socher.
|
||||
10. `CamemBERT <https://huggingface.co/transformers/model_doc/camembert.html>`_ (from FAIR, Inria, Sorbonne Université)
|
||||
released together with the paper `CamemBERT: a Tasty French Language Model <https://arxiv.org/abs/1911.03894>`_ by
|
||||
Louis Martin, Benjamin Muller, Pedro Javier Ortiz Suarez, Yoann Dupont, Laurent Romary, Eric Villemonte de la
|
||||
Clergerie, Djame Seddah, and Benoît Sagot.
|
||||
11. `ALBERT <https://github.com/google-research/ALBERT>`_ (from Google Research), released together with the paper
|
||||
`ALBERT: A Lite BERT for Self-supervised Learning of Language Representations <https://arxiv.org/abs/1909.11942>`_
|
||||
by Zhenzhong Lan, Mingda Chen, Sebastian Goodman, Kevin Gimpel, Piyush Sharma, and Radu Soricut.
|
||||
12. `T5 <https://github.com/google-research/text-to-text-transfer-transformer>`_ (from Google) released with the paper
|
||||
`Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer
|
||||
<https://arxiv.org/abs/1910.10683>`_ by Colin Raffel, Noam Shazeer, Adam Roberts, Katherine Lee, Sharan Narang,
|
||||
Michael Matena, Yanqi Zhou, Wei Li, and Peter J. Liu.
|
||||
13. `XLM-RoBERTa <https://github.com/pytorch/fairseq/tree/master/examples/xlmr>`_ (from Facebook AI), released together
|
||||
with the paper `Unsupervised Cross-lingual Representation Learning at Scale <https://arxiv.org/abs/1911.02116>`_ by
|
||||
Alexis Conneau, Kartikay Khandelwal, Naman Goyal, Vishrav Chaudhary, Guillaume Wenzek, Francisco Guzmán, Edouard
|
||||
Grave, Myle Ott, Luke Zettlemoyer, and Veselin Stoyanov.
|
||||
14. `MMBT <https://github.com/facebookresearch/mmbt/>`_ (from Facebook), released together with the paper a `Supervised
|
||||
Multimodal Bitransformers for Classifying Images and Text <https://arxiv.org/pdf/1909.02950.pdf>`_ by Douwe Kiela,
|
||||
Suvrat Bhooshan, Hamed Firooz, and Davide Testuggine.
|
||||
15. `FlauBERT <https://github.com/getalp/Flaubert>`_ (from CNRS) released with the paper `FlauBERT: Unsupervised
|
||||
Language Model Pre-training for French <https://arxiv.org/abs/1912.05372>`_ by Hang Le, Loïc Vial, Jibril Frej,
|
||||
Vincent Segonne, Maximin Coavoux, Benjamin Lecouteux, Alexandre Allauzen, Benoît Crabbé, Laurent Besacier, and
|
||||
Didier Schwab.
|
||||
16. `BART <https://github.com/pytorch/fairseq/tree/master/examples/bart>`_ (from Facebook) released with the paper
|
||||
`BART: Denoising Sequence-to-Sequence Pre-training for Natural Language Generation, Translation, and Comprehension
|
||||
<https://arxiv.org/pdf/1910.13461.pdf>`_ by Mike Lewis, Yinhan Liu, Naman Goyal, Marjan Ghazvininejad, Abdelrahman
|
||||
Mohamed, Omer Levy, Ves Stoyanov, and Luke Zettlemoyer.
|
||||
17. `ELECTRA <https://github.com/google-research/electra>`_ (from Google Research/Stanford University) released with
|
||||
the paper `ELECTRA: Pre-training text encoders as discriminators rather than generators
|
||||
<https://arxiv.org/abs/2003.10555>`_ by Kevin Clark, Minh-Thang Luong, Quoc V. Le, and Christopher D. Manning.
|
||||
18. `DialoGPT <https://github.com/microsoft/DialoGPT>`_ (from Microsoft Research) released with the paper `DialoGPT:
|
||||
Large-Scale Generative Pre-training for Conversational Response Generation <https://arxiv.org/abs/1911.00536>`_ by
|
||||
Yizhe Zhang, Siqi Sun, Michel Galley, Yen-Chun Chen, Chris Brockett, Xiang Gao, Jianfeng Gao, Jingjing Liu,
|
||||
and Bill Dolan.
|
||||
19. `Reformer <https://github.com/google/trax/tree/master/trax/models/reformer>`_ (from Google Research) released with
|
||||
the paper `Reformer: The Efficient Transformer <https://arxiv.org/abs/2001.04451>`_ by Nikita Kitaev, Łukasz
|
||||
Kaiser, and Anselm Levskaya.
|
||||
20. `MarianMT <https://marian-nmt.github.io/>`_ (developed by the Microsoft Translator Team) machine translation models
|
||||
trained using `OPUS <http://opus.nlpl.eu/>`_ pretrained_models data by Jörg Tiedemann.
|
||||
21. `Longformer <https://github.com/allenai/longformer>`_ (from AllenAI) released with the paper `Longformer: The
|
||||
Long-Document Transformer <https://arxiv.org/abs/2004.05150>`_ by Iz Beltagy, Matthew E. Peters, and Arman Cohan.
|
||||
22. `Other community models <https://huggingface.co/models>`_, contributed by the `community
|
||||
<https://huggingface.co/users>`_.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Notes
|
||||
:caption: Get started
|
||||
|
||||
installation
|
||||
quickstart
|
||||
glossary
|
||||
summary
|
||||
pretrained_models
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Using Transformers
|
||||
|
||||
usage
|
||||
summary
|
||||
serialization
|
||||
model_sharing
|
||||
multilingual
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Advanced guides
|
||||
|
||||
pretrained_models
|
||||
examples
|
||||
notebooks
|
||||
serialization
|
||||
converting_tensorflow_models
|
||||
migration
|
||||
bertology
|
||||
torchscript
|
||||
multilingual
|
||||
contributing
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Research
|
||||
|
||||
bertology
|
||||
benchmarks
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Main classes
|
||||
:caption: Package Reference
|
||||
|
||||
main_classes/configuration
|
||||
main_classes/model
|
||||
@@ -84,11 +161,6 @@ The library currently contains PyTorch and Tensorflow implementations, pre-train
|
||||
main_classes/pipelines
|
||||
main_classes/optimizer_schedules
|
||||
main_classes/processors
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Package Reference
|
||||
|
||||
model_doc/auto
|
||||
model_doc/encoderdecoder
|
||||
model_doc/bert
|
||||
@@ -111,3 +183,4 @@ The library currently contains PyTorch and Tensorflow implementations, pre-train
|
||||
model_doc/reformer
|
||||
model_doc/marian
|
||||
model_doc/longformer
|
||||
model_doc/retribert
|
||||
+76
-25
@@ -1,51 +1,102 @@
|
||||
# Installation
|
||||
|
||||
Transformers is tested on Python 3.6+ and PyTorch 1.1.0
|
||||
🤗 Transformers is tested on Python 3.6+, and PyTorch 1.1.0+ or TensorFlow 2.0+.
|
||||
|
||||
## With pip
|
||||
You should install 🤗 Transformers in a [virtual environment](https://docs.python.org/3/library/venv.html). If you're
|
||||
unfamiliar with Python virtual environments, check out the [user guide](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/). Create a virtual environment with the version of Python you're going
|
||||
to use and activate it.
|
||||
|
||||
PyTorch Transformers can be installed using pip as follows:
|
||||
Now, if you want to use 🤗 Transformers, you can install it with pip. If you'd like to play with the examples, you
|
||||
must install it from source.
|
||||
|
||||
``` bash
|
||||
## Installation with pip
|
||||
|
||||
First you need to install one of, or both, TensorFlow 2.0 and PyTorch.
|
||||
Please refer to [TensorFlow installation page](https://www.tensorflow.org/install/pip#tensorflow-2.0-rc-is-available)
|
||||
and/or [PyTorch installation page](https://pytorch.org/get-started/locally/#start-locally) regarding the specific
|
||||
install command for your platform.
|
||||
|
||||
When TensorFlow 2.0 and/or PyTorch has been installed, 🤗 Transformers can be installed using pip as follows:
|
||||
|
||||
```bash
|
||||
pip install transformers
|
||||
```
|
||||
|
||||
## From source
|
||||
Alternatively, for CPU-support only, you can install 🤗 Transformers and PyTorch in one line with
|
||||
|
||||
To install from source, clone the repository and install with:
|
||||
```bash
|
||||
pip install transformers[torch]
|
||||
```
|
||||
|
||||
or 🤗 Transformers and TensorFlow 2.0 in one line with
|
||||
|
||||
```bash
|
||||
pip install transformers[tf-cpu]
|
||||
```
|
||||
|
||||
To check 🤗 Transformers is properly installed, run the following command:
|
||||
|
||||
```bash
|
||||
python -c "from transformers import pipeline; print(pipeline('sentiment-analysis')('I hate you'))"
|
||||
```
|
||||
|
||||
It should download a pretrained model then print something like
|
||||
|
||||
```bash
|
||||
[{'label': 'NEGATIVE', 'score': 0.9991129040718079}]
|
||||
```
|
||||
|
||||
(Note that TensorFlow will print additional stuff before that last statement.)
|
||||
|
||||
## Installing from source
|
||||
|
||||
To install from source, clone the repository and install with the following commands:
|
||||
|
||||
``` bash
|
||||
git clone https://github.com/huggingface/transformers.git
|
||||
cd transformers
|
||||
pip install .
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Tests
|
||||
Again, you can run
|
||||
|
||||
An extensive test suite is included to test the library behavior and several examples. Library tests can be found in the [tests folder](https://github.com/huggingface/transformers/tree/master/tests) and examples tests in the [examples folder](https://github.com/huggingface/transformers/tree/master/examples).
|
||||
|
||||
Refer to the [contributing guide](https://github.com/huggingface/transformers/blob/master/CONTRIBUTING.md#tests) for details about running tests.
|
||||
|
||||
## OpenAI GPT original tokenization workflow
|
||||
|
||||
If you want to reproduce the original tokenization process of the `OpenAI GPT` paper, you will need to install `ftfy` and `SpaCy`:
|
||||
|
||||
``` bash
|
||||
pip install spacy ftfy==4.4.3
|
||||
python -m spacy download en
|
||||
```bash
|
||||
python -c "from transformers import pipeline; print(pipeline('sentiment-analysis')('I hate you'))"
|
||||
```
|
||||
|
||||
If you don't install `ftfy` and `SpaCy`, the `OpenAI GPT` tokenizer will default to tokenize using BERT's `BasicTokenizer` followed by Byte-Pair Encoding (which should be fine for most usage, don't worry).
|
||||
to check 🤗 Transformers is properly installed.
|
||||
|
||||
## Note on model downloads (Continuous Integration or large-scale deployments)
|
||||
## Caching models
|
||||
|
||||
If you expect to be downloading large volumes of models (more than 1,000) from our hosted bucket (for instance through your CI setup, or a large-scale production deployment), please cache the model files on your end. It will be way faster, and cheaper. Feel free to contact us privately if you need any help.
|
||||
This library provides pretrained models that will be downloaded and cached locally. Unless you specify a location with
|
||||
`cache_dir=...` when you use methods like `from_pretrained`, these models will automatically be downloaded in the
|
||||
folder given by the shell environment variable ``TRANSFORMERS_CACHE``. The default value for it will be the PyTorch
|
||||
cache home followed by ``/transformers/`` (even if you don't have PyTorch installed). This is (by order of priority):
|
||||
|
||||
* shell environment variable ``ENV_TORCH_HOME``
|
||||
* shell environment variable ``ENV_XDG_CACHE_HOME`` + ``/torch/``
|
||||
* default: ``~/.cache/torch/``
|
||||
|
||||
So if you don't have any specific environment variable set, the cache directory will be at
|
||||
``~/.cache/torch/transformers/``.
|
||||
|
||||
**Note:** If you have set a shell enviromnent variable for one of the predecessors of this library
|
||||
(``PYTORCH_TRANSFORMERS_CACHE`` or ``PYTORCH_PRETRAINED_BERT_CACHE``), those will be used if there is no shell
|
||||
enviromnent variable for ``TRANSFORMERS_CACHE``.
|
||||
|
||||
### Note on model downloads (Continuous Integration or large-scale deployments)
|
||||
|
||||
If you expect to be downloading large volumes of models (more than 1,000) from our hosted bucket (for instance through
|
||||
your CI setup, or a large-scale production deployment), please cache the model files on your end. It will be way
|
||||
faster, and cheaper. Feel free to contact us privately if you need any help.
|
||||
|
||||
## Do you want to run a Transformer model on a mobile device?
|
||||
|
||||
You should check out our [swift-coreml-transformers](https://github.com/huggingface/swift-coreml-transformers) repo.
|
||||
|
||||
It contains a set of tools to convert PyTorch or TensorFlow 2.0 trained Transformer models (currently contains `GPT-2`, `DistilGPT-2`, `BERT`, and `DistilBERT`) to CoreML models that run on iOS devices.
|
||||
It contains a set of tools to convert PyTorch or TensorFlow 2.0 trained Transformer models (currently contains `GPT-2`,
|
||||
`DistilGPT-2`, `BERT`, and `DistilBERT`) to CoreML models that run on iOS devices.
|
||||
|
||||
At some point in the future, you'll be able to seamlessly move from pre-training or fine-tuning models in PyTorch to productizing them in CoreML,
|
||||
or prototype a model or an app in CoreML then research its hyperparameters or architecture from PyTorch. Super exciting!
|
||||
At some point in the future, you'll be able to seamlessly move from pre-training or fine-tuning models in PyTorch or
|
||||
TensorFlow 2.0 to productizing them in CoreML, or prototype a model or an app in CoreML then research its
|
||||
hyperparameters or architecture from PyTorch or TensorFlow 2.0. Super exciting!
|
||||
@@ -17,7 +17,6 @@ The ``.optimization`` module provides:
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.AdamWeightDecay
|
||||
:members:
|
||||
|
||||
.. autofunction:: transformers.create_optimizer
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Recognition, Masked Language Modeling, Sentiment Analysis, Feature Extraction an
|
||||
|
||||
There are two categories of pipeline abstractions to be aware about:
|
||||
|
||||
- The :class:`~transformers.pipeline` which is the most powerful object encapsulating all other pipelines
|
||||
- The :func:`~transformers.pipeline` which is the most powerful object encapsulating all other pipelines
|
||||
- The other task-specific pipelines, such as :class:`~transformers.TokenClassificationPipeline`
|
||||
or :class:`~transformers.QuestionAnsweringPipeline`
|
||||
|
||||
@@ -17,8 +17,7 @@ The pipeline abstraction
|
||||
The `pipeline` abstraction is a wrapper around all the other available pipelines. It is instantiated as any
|
||||
other pipeline but requires an additional argument which is the `task`.
|
||||
|
||||
.. autoclass:: transformers.pipeline
|
||||
:members:
|
||||
... autofunction:: transformers.pipeline
|
||||
|
||||
|
||||
The task specific pipelines
|
||||
|
||||
@@ -17,12 +17,14 @@ The base classes ``PreTrainedTokenizer`` and ``PreTrainedTokenizerFast`` impleme
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.PreTrainedTokenizer
|
||||
:special-members: __call__
|
||||
:members:
|
||||
|
||||
``PreTrainedTokenizerFast``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.PreTrainedTokenizerFast
|
||||
:special-members: __call__
|
||||
:members:
|
||||
|
||||
``BatchEncoding``
|
||||
|
||||
@@ -67,6 +67,14 @@ AlbertForSequenceClassification
|
||||
.. autoclass:: transformers.AlbertForSequenceClassification
|
||||
:members:
|
||||
|
||||
|
||||
AlbertForMultipleChoice
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.AlbertForMultipleChoice
|
||||
:members:
|
||||
|
||||
|
||||
AlbertForTokenClassification
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -30,35 +30,35 @@ Instantiating one of ``AutoModel``, ``AutoConfig`` and ``AutoTokenizer`` will di
|
||||
|
||||
|
||||
``AutoModelForPreTraining``
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.AutoModelForPreTraining
|
||||
:members:
|
||||
|
||||
|
||||
``AutoModelWithLMHead``
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.AutoModelWithLMHead
|
||||
:members:
|
||||
|
||||
|
||||
``AutoModelForSequenceClassification``
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.AutoModelForSequenceClassification
|
||||
:members:
|
||||
|
||||
|
||||
``AutoModelForQuestionAnswering``
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.AutoModelForQuestionAnswering
|
||||
:members:
|
||||
|
||||
|
||||
``AutoModelForTokenClassification``
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.AutoModelForTokenClassification
|
||||
:members:
|
||||
|
||||
@@ -55,6 +55,13 @@ BartForSequenceClassification
|
||||
:members: forward
|
||||
|
||||
|
||||
BartForQuestionAnswering
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.BartForQuestionAnswering
|
||||
:members: forward
|
||||
|
||||
|
||||
BartForConditionalGeneration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -75,6 +75,13 @@ DistilBertForSequenceClassification
|
||||
:members:
|
||||
|
||||
|
||||
DistilBertForMultipleChoice
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.DistilBertForMultipleChoice
|
||||
:members:
|
||||
|
||||
|
||||
DistilBertForTokenClassification
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -106,6 +106,13 @@ ElectraForTokenClassification
|
||||
:members:
|
||||
|
||||
|
||||
ElectraForQuestionAnswering
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.ElectraForQuestionAnswering
|
||||
:members:
|
||||
|
||||
|
||||
TFElectraModel
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Encoder Decoder Models
|
||||
-----------
|
||||
------------------------
|
||||
|
||||
This class can wrap an encoder model, such as ``BertModel`` and a decoder modeling with a language modeling head, such as ``BertForMaskedLM`` into a encoder-decoder model.
|
||||
|
||||
@@ -10,7 +10,7 @@ An application of this architecture could be *summarization* using two pretraine
|
||||
|
||||
|
||||
``EncoderDecoderConfig``
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.EncoderDecoderConfig
|
||||
:members:
|
||||
|
||||
@@ -38,6 +38,17 @@ Hugging Face showcasing the generative capabilities of several models. GPT is on
|
||||
|
||||
The original code can be found `here <https://github.com/openai/finetune-transformer-lm>`_.
|
||||
|
||||
Note:
|
||||
|
||||
If you want to reproduce the original tokenization process of the `OpenAI GPT` paper, you will need to install
|
||||
``ftfy`` and ``SpaCy``::
|
||||
|
||||
pip install spacy ftfy==4.4.3
|
||||
python -m spacy download en
|
||||
|
||||
If you don't install ``ftfy`` and ``SpaCy``, the :class:`transformers.OpenAIGPTTokenizer` will default to tokenize using
|
||||
BERT's :obj:`BasicTokenizer` followed by Byte-Pair Encoding (which should be fine for most usage, don't
|
||||
worry).
|
||||
|
||||
OpenAIGPTConfig
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -4,7 +4,7 @@ Reformer
|
||||
file a `Github Issue <https://github.com/huggingface/transformers/issues/new?assignees=&labels=&template=bug-report.md&title>`_
|
||||
|
||||
Overview
|
||||
~~~~~
|
||||
~~~~~~~~~~
|
||||
The Reformer model was presented in `Reformer: The Efficient Transformer <https://arxiv.org/abs/2001.04451.pdf>`_ by Nikita Kitaev, Łukasz Kaiser, Anselm Levskaya.
|
||||
Here the abstract:
|
||||
|
||||
@@ -13,7 +13,7 @@ Here the abstract:
|
||||
The Authors' code can be found `here <https://github.com/google/trax/tree/master/trax/models/reformer>`_ .
|
||||
|
||||
Axial Positional Encodings
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Axial Positional Encodings were first implemented in Google's `trax library <https://github.com/google/trax/blob/4d99ad4965bab1deba227539758d59f0df0fef48/trax/layers/research/position_encodings.py#L29>`_ and developed by the authors of this model's paper. In models that are treating very long input sequences, the conventional position id encodings store an embedings vector of size :math:`d` being the ``config.hidden_size`` for every position :math:`i, \ldots, n_s`, with :math:`n_s` being ``config.max_embedding_size``. *E.g.*, having a sequence length of :math:`n_s = 2^{19} \approx 0.5M` and a ``config.hidden_size`` of :math:`d = 2^{10} \approx 1000` would result in a position encoding matrix:
|
||||
|
||||
.. math::
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
RetriBERT
|
||||
----------------------------------------------------
|
||||
|
||||
Overview
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The RetriBERT model was proposed in the blog post
|
||||
`Explain Anything Like I'm Five: A Model for Open Domain Long Form Question Answering <https://yjernite.github.io/lfqa.html>`__,
|
||||
RetriBERT is a small model that uses either a single or pair of Bert encoders with lower-dimension projection for dense semantic indexing of text.
|
||||
|
||||
Code to train and use the model can be found `here <https://github.com/huggingface/transformers/tree/master/examples/distillation>`_.
|
||||
|
||||
|
||||
RetriBertConfig
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.RetriBertConfig
|
||||
:members:
|
||||
|
||||
|
||||
RetriBertTokenizer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.RetriBertTokenizer
|
||||
:members:
|
||||
|
||||
|
||||
RetriBertTokenizerFast
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.RetriBertTokenizerFast
|
||||
:members:
|
||||
|
||||
|
||||
RetriBertModel
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: transformers.RetriBertModel
|
||||
:members:
|
||||
@@ -31,7 +31,7 @@ Training
|
||||
|
||||
T5 is an encoder-decoder model and converts all NLP problems into a text-to-text format. It is trained using teacher forcing.
|
||||
This means that for training we always need an input sequence and a target sequence.
|
||||
The input sequence is fed to the model using ``input_ids``. The target sequence is shifted to the right, *i.e.* prepended by a start-sequence token and fed to the decoder using the `decoder_input_ids`. In teacher-forcing style, the target sequence is then appended by the EOS token and corresponds to the ``lm_labels``. The PAD token is hereby used as the start-sequence token.
|
||||
The input sequence is fed to the model using ``input_ids``. The target sequence is shifted to the right, *i.e.* prepended by a start-sequence token and fed to the decoder using the `decoder_input_ids`. In teacher-forcing style, the target sequence is then appended by the EOS token and corresponds to the ``labels``. The PAD token is hereby used as the start-sequence token.
|
||||
T5 can be trained / fine-tuned both in a supervised and unsupervised fashion.
|
||||
|
||||
- Unsupervised denoising training
|
||||
@@ -44,9 +44,9 @@ T5 can be trained / fine-tuned both in a supervised and unsupervised fashion.
|
||||
::
|
||||
|
||||
input_ids = tokenizer.encode('The <extra_id_1> walks in <extra_id_2> park', return_tensors='pt')
|
||||
lm_labels = tokenizer.encode('<extra_id_1> cute dog <extra_id_2> the <extra_id_3> </s>', return_tensors='pt')
|
||||
labels = tokenizer.encode('<extra_id_1> cute dog <extra_id_2> the <extra_id_3> </s>', return_tensors='pt')
|
||||
# the forward function automatically creates the correct decoder_input_ids
|
||||
model(input_ids=input_ids, lm_labels=lm_labels)
|
||||
model(input_ids=input_ids, labels=labels)
|
||||
|
||||
- Supervised training
|
||||
|
||||
@@ -57,9 +57,9 @@ T5 can be trained / fine-tuned both in a supervised and unsupervised fashion.
|
||||
::
|
||||
|
||||
input_ids = tokenizer.encode('translate English to German: The house is wonderful. </s>', return_tensors='pt')
|
||||
lm_labels = tokenizer.encode('Das Haus ist wunderbar. </s>', return_tensors='pt')
|
||||
labels = tokenizer.encode('Das Haus ist wunderbar. </s>', return_tensors='pt')
|
||||
# the forward function automatically creates the correct decoder_input_ids
|
||||
model(input_ids=input_ids, lm_labels=lm_labels)
|
||||
model(input_ids=input_ids, labels=labels)
|
||||
|
||||
|
||||
T5Config
|
||||
|
||||
@@ -22,10 +22,12 @@ For a list that includes community-uploaded models, refer to `https://huggingfac
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-base-multilingual-uncased`` | | (Original, not recommended) 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on lower-cased text in the top 102 languages with the largest Wikipedias |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/bert/blob/master/multilingual.md>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-base-multilingual-cased`` | | (New, **recommended**) 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on cased text in the top 104 languages with the largest Wikipedias |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/bert/blob/master/multilingual.md>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-base-chinese`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
@@ -33,64 +35,79 @@ For a list that includes community-uploaded models, refer to `https://huggingfac
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-base-german-cased`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on cased German text by Deepset.ai |
|
||||
| | | |
|
||||
| | | (see `details on deepset.ai website <https://deepset.ai/german-bert>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-large-uncased-whole-word-masking`` | | 24-layer, 1024-hidden, 16-heads, 340M parameters. |
|
||||
| | | | Trained on lower-cased English text using Whole-Word-Masking |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/bert/#bert>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-large-cased-whole-word-masking`` | | 24-layer, 1024-hidden, 16-heads, 340M parameters. |
|
||||
| | | | Trained on cased English text using Whole-Word-Masking |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/bert/#bert>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-large-uncased-whole-word-masking-finetuned-squad`` | | 24-layer, 1024-hidden, 16-heads, 340M parameters. |
|
||||
| | | | The ``bert-large-uncased-whole-word-masking`` model fine-tuned on SQuAD |
|
||||
| | | |
|
||||
| | | (see details of fine-tuning in the `example section <https://github.com/huggingface/transformers/tree/master/examples>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-large-cased-whole-word-masking-finetuned-squad`` | | 24-layer, 1024-hidden, 16-heads, 340M parameters |
|
||||
| | | | The ``bert-large-cased-whole-word-masking`` model fine-tuned on SQuAD |
|
||||
| | | |
|
||||
| | | (see `details of fine-tuning in the example section <https://huggingface.co/transformers/examples.html>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-base-cased-finetuned-mrpc`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | The ``bert-base-cased`` model fine-tuned on MRPC |
|
||||
| | | |
|
||||
| | | (see `details of fine-tuning in the example section <https://huggingface.co/transformers/examples.html>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-base-german-dbmdz-cased`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on cased German text by DBMDZ |
|
||||
| | | |
|
||||
| | | (see `details on dbmdz repository <https://github.com/dbmdz/german-bert>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``bert-base-german-dbmdz-uncased`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on uncased German text by DBMDZ |
|
||||
| | | |
|
||||
| | | (see `details on dbmdz repository <https://github.com/dbmdz/german-bert>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``cl-tohoku/bert-base-japanese`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on Japanese text. Text is tokenized with MeCab and WordPiece. |
|
||||
| | | | `MeCab <https://taku910.github.io/mecab/>`__ is required for tokenization. |
|
||||
| | | |
|
||||
| | | (see `details on cl-tohoku repository <https://github.com/cl-tohoku/bert-japanese>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``cl-tohoku/bert-base-japanese-whole-word-masking`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on Japanese text using Whole-Word-Masking. Text is tokenized with MeCab and WordPiece. |
|
||||
| | | | `MeCab <https://taku910.github.io/mecab/>`__ is required for tokenization. |
|
||||
| | | |
|
||||
| | | (see `details on cl-tohoku repository <https://github.com/cl-tohoku/bert-japanese>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``cl-tohoku/bert-base-japanese-char`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on Japanese text. Text is tokenized into characters. |
|
||||
| | | |
|
||||
| | | (see `details on cl-tohoku repository <https://github.com/cl-tohoku/bert-japanese>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``cl-tohoku/bert-base-japanese-char-whole-word-masking`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on Japanese text using Whole-Word-Masking. Text is tokenized into characters. |
|
||||
| | | |
|
||||
| | | (see `details on cl-tohoku repository <https://github.com/cl-tohoku/bert-japanese>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``TurkuNLP/bert-base-finnish-cased-v1`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on cased Finnish text. |
|
||||
| | | |
|
||||
| | | (see `details on turkunlp.org <http://turkunlp.org/FinBERT/>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``TurkuNLP/bert-base-finnish-uncased-v1`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on uncased Finnish text. |
|
||||
| | | |
|
||||
| | | (see `details on turkunlp.org <http://turkunlp.org/FinBERT/>`__). |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``wietsedv/bert-base-dutch-cased`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
| | | | Trained on cased Dutch text. |
|
||||
| | | |
|
||||
| | | (see `details on wietsedv repository <https://github.com/wietsedv/bertje/>`__). |
|
||||
+-------------------+------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| GPT | ``openai-gpt`` | | 12-layer, 768-hidden, 12-heads, 110M parameters. |
|
||||
@@ -149,54 +166,67 @@ For a list that includes community-uploaded models, refer to `https://huggingfac
|
||||
+-------------------+------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| RoBERTa | ``roberta-base`` | | 12-layer, 768-hidden, 12-heads, 125M parameters |
|
||||
| | | | RoBERTa using the BERT-base architecture |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/pytorch/fairseq/tree/master/examples/roberta>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``roberta-large`` | | 24-layer, 1024-hidden, 16-heads, 355M parameters |
|
||||
| | | | RoBERTa using the BERT-large architecture |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/pytorch/fairseq/tree/master/examples/roberta>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``roberta-large-mnli`` | | 24-layer, 1024-hidden, 16-heads, 355M parameters |
|
||||
| | | | ``roberta-large`` fine-tuned on `MNLI <http://www.nyu.edu/projects/bowman/multinli/>`__. |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/pytorch/fairseq/tree/master/examples/roberta>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``distilroberta-base`` | | 6-layer, 768-hidden, 12-heads, 82M parameters |
|
||||
| | | | The DistilRoBERTa model distilled from the RoBERTa model `roberta-base` checkpoint. |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/huggingface/transformers/tree/master/examples/distillation>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``roberta-base-openai-detector`` | | 12-layer, 768-hidden, 12-heads, 125M parameters |
|
||||
| | | | ``roberta-base`` fine-tuned by OpenAI on the outputs of the 1.5B-parameter GPT-2 model. |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/openai/gpt-2-output-dataset/tree/master/detector>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``roberta-large-openai-detector`` | | 24-layer, 1024-hidden, 16-heads, 355M parameters |
|
||||
| | | | ``roberta-large`` fine-tuned by OpenAI on the outputs of the 1.5B-parameter GPT-2 model. |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/openai/gpt-2-output-dataset/tree/master/detector>`__) |
|
||||
+-------------------+------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| DistilBERT | ``distilbert-base-uncased`` | | 6-layer, 768-hidden, 12-heads, 66M parameters |
|
||||
| | | | The DistilBERT model distilled from the BERT model `bert-base-uncased` checkpoint |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/huggingface/transformers/tree/master/examples/distillation>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``distilbert-base-uncased-distilled-squad`` | | 6-layer, 768-hidden, 12-heads, 66M parameters |
|
||||
| | | | The DistilBERT model distilled from the BERT model `bert-base-uncased` checkpoint, with an additional linear layer. |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/huggingface/transformers/tree/master/examples/distillation>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``distilbert-base-cased`` | | 6-layer, 768-hidden, 12-heads, 65M parameters |
|
||||
| | | | The DistilBERT model distilled from the BERT model `bert-base-cased` checkpoint |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/huggingface/transformers/tree/master/examples/distillation>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``distilbert-base-cased-distilled-squad`` | | 6-layer, 768-hidden, 12-heads, 65M parameters |
|
||||
| | | | The DistilBERT model distilled from the BERT model `bert-base-cased` checkpoint, with an additional question answering layer. |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/huggingface/transformers/tree/master/examples/distillation>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``distilgpt2`` | | 6-layer, 768-hidden, 12-heads, 82M parameters |
|
||||
| | | | The DistilGPT2 model distilled from the GPT2 model `gpt2` checkpoint. |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/huggingface/transformers/tree/master/examples/distillation>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``distilbert-base-german-cased`` | | 6-layer, 768-hidden, 12-heads, 66M parameters |
|
||||
| | | | The German DistilBERT model distilled from the German DBMDZ BERT model `bert-base-german-dbmdz-cased` checkpoint. |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/huggingface/transformers/tree/master/examples/distillation>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``distilbert-base-multilingual-cased`` | | 6-layer, 768-hidden, 12-heads, 134M parameters |
|
||||
| | | | The multilingual DistilBERT model distilled from the Multilingual BERT model `bert-base-multilingual-cased` checkpoint. |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/huggingface/transformers/tree/master/examples/distillation>`__) |
|
||||
+-------------------+------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| CTRL | ``ctrl`` | | 48-layer, 1280-hidden, 16-heads, 1.6B parameters |
|
||||
@@ -204,38 +234,47 @@ For a list that includes community-uploaded models, refer to `https://huggingfac
|
||||
+-------------------+------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| CamemBERT | ``camembert-base`` | | 12-layer, 768-hidden, 12-heads, 110M parameters |
|
||||
| | | | CamemBERT using the BERT-base architecture |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/pytorch/fairseq/tree/master/examples/camembert>`__) |
|
||||
+-------------------+------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| ALBERT | ``albert-base-v1`` | | 12 repeating layers, 128 embedding, 768-hidden, 12-heads, 11M parameters |
|
||||
| | | | ALBERT base model |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/ALBERT>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``albert-large-v1`` | | 24 repeating layers, 128 embedding, 1024-hidden, 16-heads, 17M parameters |
|
||||
| | | | ALBERT large model |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/ALBERT>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``albert-xlarge-v1`` | | 24 repeating layers, 128 embedding, 2048-hidden, 16-heads, 58M parameters |
|
||||
| | | | ALBERT xlarge model |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/ALBERT>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``albert-xxlarge-v1`` | | 12 repeating layer, 128 embedding, 4096-hidden, 64-heads, 223M parameters |
|
||||
| | | | ALBERT xxlarge model |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/ALBERT>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``albert-base-v2`` | | 12 repeating layers, 128 embedding, 768-hidden, 12-heads, 11M parameters |
|
||||
| | | | ALBERT base model with no dropout, additional training data and longer training |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/ALBERT>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``albert-large-v2`` | | 24 repeating layers, 128 embedding, 1024-hidden, 16-heads, 17M parameters |
|
||||
| | | | ALBERT large model with no dropout, additional training data and longer training |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/ALBERT>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``albert-xlarge-v2`` | | 24 repeating layers, 128 embedding, 2048-hidden, 16-heads, 58M parameters |
|
||||
| | | | ALBERT xlarge model with no dropout, additional training data and longer training |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/ALBERT>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``albert-xxlarge-v2`` | | 12 repeating layer, 128 embedding, 4096-hidden, 64-heads, 223M parameters |
|
||||
| | | | ALBERT xxlarge model with no dropout, additional training data and longer training |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/google-research/ALBERT>`__) |
|
||||
+-------------------+------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| T5 | ``t5-small`` | | ~60M parameters with 6-layers, 512-hidden-state, 2048 feed-forward hidden-state, 8-heads, |
|
||||
@@ -261,23 +300,30 @@ For a list that includes community-uploaded models, refer to `https://huggingfac
|
||||
+-------------------+------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| FlauBERT | ``flaubert/flaubert_small_cased`` | | 6-layer, 512-hidden, 8-heads, 54M parameters |
|
||||
| | | | FlauBERT small architecture |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/getalp/Flaubert>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``flaubert/flaubert_base_uncased`` | | 12-layer, 768-hidden, 12-heads, 137M parameters |
|
||||
| | | | FlauBERT base architecture with uncased vocabulary |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/getalp/Flaubert>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``flaubert/flaubert_base_cased`` | | 12-layer, 768-hidden, 12-heads, 138M parameters |
|
||||
| | | | FlauBERT base architecture with cased vocabulary |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/getalp/Flaubert>`__) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``flaubert/flaubert_large_cased`` | | 24-layer, 1024-hidden, 16-heads, 373M parameters |
|
||||
| | | | FlauBERT large architecture |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/getalp/Flaubert>`__) |
|
||||
+-------------------+------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Bart | ``facebook/bart-large`` | | 24-layer, 1024-hidden, 16-heads, 406M parameters |
|
||||
| | | |
|
||||
| | | (see `details <https://github.com/pytorch/fairseq/tree/master/examples/bart>`_) |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``facebook/bart-base`` | | 12-layer, 768-hidden, 16-heads, 139M parameters |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``facebook/bart-large-mnli`` | | Adds a 2 layer classification head with 1 million parameters |
|
||||
| | | | bart-large base architecture with a classification head, finetuned on MNLI |
|
||||
| +------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
@@ -1,104 +1,3 @@
|
||||
Loading Google AI or OpenAI pre-trained weights or PyTorch dump
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``from_pretrained()`` method
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To load one of Google AI's, OpenAI's pre-trained models or a PyTorch saved model (an instance of ``BertForPreTraining`` saved with ``torch.save()``\ ), the PyTorch model classes and the tokenizer can be instantiated using the ``from_pretrained()`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
model = BERT_CLASS.from_pretrained(PRE_TRAINED_MODEL_NAME_OR_PATH, cache_dir=None, from_tf=False, state_dict=None, *input, **kwargs)
|
||||
|
||||
where
|
||||
|
||||
|
||||
* ``BERT_CLASS`` is either a tokenizer to load the vocabulary (\ ``BertTokenizer`` or ``OpenAIGPTTokenizer`` classes) or one of the eight BERT or three OpenAI GPT PyTorch model classes (to load the pre-trained weights): ``BertModel``\ , ``BertForMaskedLM``\ , ``BertForNextSentencePrediction``\ , ``BertForPreTraining``\ , ``BertForSequenceClassification``\ , ``BertForTokenClassification``\ , ``BertForMultipleChoice``\ , ``BertForQuestionAnswering``\ , ``OpenAIGPTModel``\ , ``OpenAIGPTLMHeadModel`` or ``OpenAIGPTDoubleHeadsModel``\ , and
|
||||
*
|
||||
``PRE_TRAINED_MODEL_NAME_OR_PATH`` is either:
|
||||
|
||||
|
||||
*
|
||||
the shortcut name of a Google AI's or OpenAI's pre-trained model selected in the list:
|
||||
|
||||
|
||||
* ``bert-base-uncased``: 12-layer, 768-hidden, 12-heads, 110M parameters
|
||||
* ``bert-large-uncased``: 24-layer, 1024-hidden, 16-heads, 340M parameters
|
||||
* ``bert-base-cased``: 12-layer, 768-hidden, 12-heads , 110M parameters
|
||||
* ``bert-large-cased``: 24-layer, 1024-hidden, 16-heads, 340M parameters
|
||||
* ``bert-base-multilingual-uncased``: (Orig, not recommended) 102 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
|
||||
* ``bert-base-multilingual-cased``: **(New, recommended)** 104 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
|
||||
* ``bert-base-chinese``: Chinese Simplified and Traditional, 12-layer, 768-hidden, 12-heads, 110M parameters
|
||||
* ``bert-base-german-cased``: Trained on German data only, 12-layer, 768-hidden, 12-heads, 110M parameters `Performance Evaluation <https://deepset.ai/german-bert>`__
|
||||
* ``bert-large-uncased-whole-word-masking``: 24-layer, 1024-hidden, 16-heads, 340M parameters - Trained with Whole Word Masking (mask all of the the tokens corresponding to a word at once)
|
||||
* ``bert-large-cased-whole-word-masking``: 24-layer, 1024-hidden, 16-heads, 340M parameters - Trained with Whole Word Masking (mask all of the the tokens corresponding to a word at once)
|
||||
* ``bert-large-uncased-whole-word-masking-finetuned-squad``: The ``bert-large-uncased-whole-word-masking`` model finetuned on SQuAD (using the ``run_bert_squad.py`` examples). Results: *exact_match: 86.91579943235573, f1: 93.1532499015869*
|
||||
* ``bert-base-german-dbmdz-cased``: Trained on German data only, 12-layer, 768-hidden, 12-heads, 110M parameters `Performance Evaluation <https://github.com/dbmdz/german-bert>`__
|
||||
* ``bert-base-german-dbmdz-uncased``: Trained on (uncased) German data only, 12-layer, 768-hidden, 12-heads, 110M parameters `Performance Evaluation <https://github.com/dbmdz/german-bert>`__
|
||||
* ``openai-gpt``: OpenAI GPT English model, 12-layer, 768-hidden, 12-heads, 110M parameters
|
||||
* ``gpt2``: OpenAI GPT-2 English model, 12-layer, 768-hidden, 12-heads, 117M parameters
|
||||
* ``gpt2-medium``: OpenAI GPT-2 English model, 24-layer, 1024-hidden, 16-heads, 345M parameters
|
||||
* ``transfo-xl-wt103``: Transformer-XL English model trained on wikitext-103, 18-layer, 1024-hidden, 16-heads, 257M parameters
|
||||
|
||||
*
|
||||
a path or url to a pretrained model archive containing:
|
||||
|
||||
|
||||
* ``bert_config.json`` or ``openai_gpt_config.json`` a configuration file for the model, and
|
||||
* ``pytorch_model.bin`` a PyTorch dump of a pre-trained instance of ``BertForPreTraining``\ , ``OpenAIGPTModel``\ , ``TransfoXLModel``\ , ``GPT2LMHeadModel`` (saved with the usual ``torch.save()``\ )
|
||||
|
||||
If ``PRE_TRAINED_MODEL_NAME_OR_PATH`` is a shortcut name, the pre-trained weights will be downloaded from AWS S3 (see the links `here <https://github.com/huggingface/transformers/blob/master/transformers/modeling_bert.py>`__\ ) and stored in a cache folder to avoid future download (the cache folder can be found at ``~/.pytorch_pretrained_bert/``\ ).
|
||||
|
||||
*
|
||||
``cache_dir`` can be an optional path to a specific directory to download and cache the pre-trained model weights. This option is useful in particular when you are using distributed training: to avoid concurrent access to the same weights you can set for example ``cache_dir='./pretrained_model_{}'.format(args.local_rank)`` (see the section on distributed training for more information).
|
||||
|
||||
* ``from_tf``\ : should we load the weights from a locally saved TensorFlow checkpoint
|
||||
* ``state_dict``\ : an optional state dictionary (collections.OrderedDict object) to use instead of Google pre-trained models
|
||||
* ``*inputs``\ , `**kwargs`: additional input for the specific Bert class (ex: num_labels for BertForSequenceClassification)
|
||||
|
||||
``Uncased`` means that the text has been lowercased before WordPiece tokenization, e.g., ``John Smith`` becomes ``john smith``. The Uncased model also strips out any accent markers. ``Cased`` means that the true case and accent markers are preserved. Typically, the Uncased model is better unless you know that case information is important for your task (e.g., Named Entity Recognition or Part-of-Speech tagging). For information about the Multilingual and Chinese model, see the `Multilingual README <https://github.com/google-research/bert/blob/master/multilingual.md>`__ or the original TensorFlow repository.
|
||||
|
||||
When using an ``uncased model``\ , make sure your tokenizer has ``do_lower_case=True`` (either in its configuration, or passed as an additional parameter).
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# BERT
|
||||
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_basic_tokenize=True)
|
||||
model = BertForSequenceClassification.from_pretrained('bert-base-uncased')
|
||||
|
||||
# OpenAI GPT
|
||||
tokenizer = OpenAIGPTTokenizer.from_pretrained('openai-gpt')
|
||||
model = OpenAIGPTModel.from_pretrained('openai-gpt')
|
||||
|
||||
# Transformer-XL
|
||||
tokenizer = TransfoXLTokenizer.from_pretrained('transfo-xl-wt103')
|
||||
model = TransfoXLModel.from_pretrained('transfo-xl-wt103')
|
||||
|
||||
# OpenAI GPT-2
|
||||
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
|
||||
model = GPT2Model.from_pretrained('gpt2')
|
||||
|
||||
Cache directory
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
``pytorch_pretrained_bert`` save the pretrained weights in a cache directory which is located at (in this order of priority):
|
||||
|
||||
|
||||
* ``cache_dir`` optional arguments to the ``from_pretrained()`` method (see above),
|
||||
* shell environment variable ``PYTORCH_PRETRAINED_BERT_CACHE``\ ,
|
||||
* PyTorch cache home + ``/pytorch_pretrained_bert/``
|
||||
where PyTorch cache home is defined by (in this order):
|
||||
|
||||
* shell environment variable ``ENV_TORCH_HOME``
|
||||
* shell environment variable ``ENV_XDG_CACHE_HOME`` + ``/torch/``\ )
|
||||
* default: ``~/.cache/torch/``
|
||||
|
||||
Usually, if you don't set any specific environment variable, ``pytorch_pretrained_bert`` cache will be at ``~/.cache/torch/pytorch_pretrained_bert/``.
|
||||
|
||||
You can alsways safely delete ``pytorch_pretrained_bert`` cache but the pretrained model weights and vocabulary files wil have to be re-downloaded from our S3.
|
||||
|
||||
Serialization best-practices
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -692,7 +692,8 @@ following array should be the output:
|
||||
|
||||
::
|
||||
|
||||
[('[CLS]', 'O'), ('Hu', 'I-ORG'), ('##gging', 'I-ORG'), ('Face', 'I-ORG'), ('Inc', 'I-ORG'), ('.', 'O'), ('is', 'O'), ('a', 'O'), ('company', 'O'), ('based', 'O'), ('in', 'O'), ('New', 'I-LOC'), ('York', 'I-LOC'), ('City', 'I-LOC'), ('.', 'O'), ('Its', 'O'), ('headquarters', 'O'), ('are', 'O'), ('in', 'O'), ('D', 'I-LOC'), ('##UM', 'I-LOC'), ('##BO', 'I-LOC'), (',', 'O'), ('therefore', 'O'), ('very', 'O'), ('##c', 'O'), ('##lose', 'O'), ('to', 'O'), ('the', 'O'), ('Manhattan', 'I-LOC'), ('Bridge', 'I-LOC'), ('.', 'O'), ('[SEP]', 'O')]
|
||||
[('[CLS]', 'O'), ('Hu', 'I-ORG'), ('##gging', 'I-ORG'), ('Face', 'I-ORG'), ('Inc', 'I-ORG'), ('.', 'O'), ('is', 'O'), ('a', 'O'), ('company', 'O'), ('based', 'O'), ('in', 'O'), ('New', 'I-LOC'), ('York', 'I-LOC'), ('City', 'I-LOC'), ('.', 'O'), ('Its', 'O'), ('headquarters', 'O'), ('are', 'O'), ('in', 'O'), ('D', 'I-LOC'), ('##UM', 'I-LOC'), ('##BO', 'I-LOC'), (',', 'O'), ('therefore', 'O'), ('very', 'O'), ('##c', 'O'), ('##lose', 'O'), ('to', 'O'), ('the', 'O'), ('Manhattan', 'I-LOC'), ('Bridge', 'I-LOC'), ('.', 'O'), ('[SEP]', 'O')]
|
||||
|
||||
Summarization
|
||||
----------------------------------------------------
|
||||
|
||||
@@ -769,7 +770,8 @@ Here Google`s T5 model is used that was only pre-trained on a multi-task mixed d
|
||||
# T5 uses a max_length of 512 so we cut the article to 512 tokens.
|
||||
inputs = tokenizer.encode("summarize: " + ARTICLE, return_tensors="tf", max_length=512)
|
||||
outputs = model.generate(inputs, max_length=150, min_length=40, length_penalty=2.0, num_beams=4, early_stopping=True)
|
||||
print(outputs)
|
||||
print(outputs)
|
||||
|
||||
Translation
|
||||
----------------------------------------------------
|
||||
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ This is still a work-in-progress – in particular documentation is still sparse
|
||||
| [**`summarization`**](https://github.com/huggingface/transformers/tree/master/examples/summarization) | CNN/Daily Mail | - | - | - | -
|
||||
| [**`translation`**](https://github.com/huggingface/transformers/tree/master/examples/translation) | WMT | - | - | - | -
|
||||
| [**`bertology`**](https://github.com/huggingface/transformers/tree/master/examples/bertology) | - | - | - | - | -
|
||||
| [**`adversarial`**](https://github.com/huggingface/transformers/tree/master/examples/adversarial) | HANS | - | - | - | -
|
||||
| [**`adversarial`**](https://github.com/huggingface/transformers/tree/master/examples/adversarial) | HANS | ✅ | - | - | -
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
@@ -11,7 +11,7 @@ export HANS_DIR=path-to-hans
|
||||
export MODEL_TYPE=type-of-the-model-e.g.-bert-roberta-xlnet-etc
|
||||
export MODEL_PATH=path-to-the-model-directory-that-is-trained-on-NLI-e.g.-by-using-run_glue.py
|
||||
|
||||
python examples/hans/test_hans.py \
|
||||
python run_hans.py \
|
||||
--task_name hans \
|
||||
--model_type $MODEL_TYPE \
|
||||
--do_eval \
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team.
|
||||
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
""" GLUE processors and helpers """
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from transformers.file_utils import is_tf_available
|
||||
from utils_hans import DataProcessor, InputExample, InputFeatures
|
||||
|
||||
|
||||
if is_tf_available():
|
||||
import tensorflow as tf
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def hans_convert_examples_to_features(
|
||||
examples,
|
||||
tokenizer,
|
||||
max_length=512,
|
||||
task=None,
|
||||
label_list=None,
|
||||
output_mode=None,
|
||||
pad_on_left=False,
|
||||
pad_token=0,
|
||||
pad_token_segment_id=0,
|
||||
mask_padding_with_zero=True,
|
||||
):
|
||||
"""
|
||||
Loads a data file into a list of ``InputFeatures``
|
||||
|
||||
Args:
|
||||
examples: List of ``InputExamples`` or ``tf.data.Dataset`` containing the examples.
|
||||
tokenizer: Instance of a tokenizer that will tokenize the examples
|
||||
max_length: Maximum example length
|
||||
task: HANS
|
||||
label_list: List of labels. Can be obtained from the processor using the ``processor.get_labels()`` method
|
||||
output_mode: String indicating the output mode. Either ``regression`` or ``classification``
|
||||
pad_on_left: If set to ``True``, the examples will be padded on the left rather than on the right (default)
|
||||
pad_token: Padding token
|
||||
pad_token_segment_id: The segment ID for the padding token (It is usually 0, but can vary such as for XLNet where it is 4)
|
||||
mask_padding_with_zero: If set to ``True``, the attention mask will be filled by ``1`` for actual values
|
||||
and by ``0`` for padded values. If set to ``False``, inverts it (``1`` for padded values, ``0`` for
|
||||
actual values)
|
||||
|
||||
Returns:
|
||||
If the ``examples`` input is a ``tf.data.Dataset``, will return a ``tf.data.Dataset``
|
||||
containing the task-specific features. If the input is a list of ``InputExamples``, will return
|
||||
a list of task-specific ``InputFeatures`` which can be fed to the model.
|
||||
|
||||
"""
|
||||
is_tf_dataset = False
|
||||
if is_tf_available() and isinstance(examples, tf.data.Dataset):
|
||||
is_tf_dataset = True
|
||||
|
||||
if task is not None:
|
||||
processor = glue_processors[task]()
|
||||
if label_list is None:
|
||||
label_list = processor.get_labels()
|
||||
logger.info("Using label list %s for task %s" % (label_list, task))
|
||||
if output_mode is None:
|
||||
output_mode = glue_output_modes[task]
|
||||
logger.info("Using output mode %s for task %s" % (output_mode, task))
|
||||
|
||||
label_map = {label: i for i, label in enumerate(label_list)}
|
||||
|
||||
features = []
|
||||
for (ex_index, example) in enumerate(examples):
|
||||
if ex_index % 10000 == 0:
|
||||
logger.info("Writing example %d" % (ex_index))
|
||||
if is_tf_dataset:
|
||||
example = processor.get_example_from_tensor_dict(example)
|
||||
example = processor.tfds_map(example)
|
||||
|
||||
inputs = tokenizer.encode_plus(example.text_a, example.text_b, add_special_tokens=True, max_length=max_length,)
|
||||
input_ids, token_type_ids = inputs["input_ids"], inputs["token_type_ids"]
|
||||
|
||||
# The mask has 1 for real tokens and 0 for padding tokens. Only real
|
||||
# tokens are attended to.
|
||||
attention_mask = [1 if mask_padding_with_zero else 0] * len(input_ids)
|
||||
|
||||
# Zero-pad up to the sequence length.
|
||||
padding_length = max_length - len(input_ids)
|
||||
if pad_on_left:
|
||||
input_ids = ([pad_token] * padding_length) + input_ids
|
||||
attention_mask = ([0 if mask_padding_with_zero else 1] * padding_length) + attention_mask
|
||||
token_type_ids = ([pad_token_segment_id] * padding_length) + token_type_ids
|
||||
else:
|
||||
input_ids = input_ids + ([pad_token] * padding_length)
|
||||
attention_mask = attention_mask + ([0 if mask_padding_with_zero else 1] * padding_length)
|
||||
token_type_ids = token_type_ids + ([pad_token_segment_id] * padding_length)
|
||||
|
||||
assert len(input_ids) == max_length, "Error with input length {} vs {}".format(len(input_ids), max_length)
|
||||
assert len(attention_mask) == max_length, "Error with input length {} vs {}".format(
|
||||
len(attention_mask), max_length
|
||||
)
|
||||
assert len(token_type_ids) == max_length, "Error with input length {} vs {}".format(
|
||||
len(token_type_ids), max_length
|
||||
)
|
||||
|
||||
if output_mode == "classification":
|
||||
label = label_map[example.label] if example.label in label_map else 0
|
||||
elif output_mode == "regression":
|
||||
label = float(example.label)
|
||||
else:
|
||||
raise KeyError(output_mode)
|
||||
pairID = str(example.pairID)
|
||||
|
||||
if ex_index < 10:
|
||||
logger.info("*** Example ***")
|
||||
logger.info("text_a: %s" % (example.text_a))
|
||||
logger.info("text_b: %s" % (example.text_b))
|
||||
logger.info("guid: %s" % (example.guid))
|
||||
logger.info("input_ids: %s" % " ".join([str(x) for x in input_ids]))
|
||||
logger.info("attention_mask: %s" % " ".join([str(x) for x in attention_mask]))
|
||||
logger.info("token_type_ids: %s" % " ".join([str(x) for x in token_type_ids]))
|
||||
logger.info("label: %s (id = %d)" % (example.label, label))
|
||||
|
||||
features.append(
|
||||
InputFeatures(
|
||||
input_ids=input_ids,
|
||||
attention_mask=attention_mask,
|
||||
token_type_ids=token_type_ids,
|
||||
label=label,
|
||||
pairID=pairID,
|
||||
)
|
||||
)
|
||||
|
||||
if is_tf_available() and is_tf_dataset:
|
||||
|
||||
def gen():
|
||||
for ex in features:
|
||||
yield (
|
||||
{
|
||||
"input_ids": ex.input_ids,
|
||||
"attention_mask": ex.attention_mask,
|
||||
"token_type_ids": ex.token_type_ids,
|
||||
},
|
||||
ex.label,
|
||||
)
|
||||
|
||||
return tf.data.Dataset.from_generator(
|
||||
gen,
|
||||
({"input_ids": tf.int32, "attention_mask": tf.int32, "token_type_ids": tf.int32}, tf.int64),
|
||||
(
|
||||
{
|
||||
"input_ids": tf.TensorShape([None]),
|
||||
"attention_mask": tf.TensorShape([None]),
|
||||
"token_type_ids": tf.TensorShape([None]),
|
||||
},
|
||||
tf.TensorShape([]),
|
||||
),
|
||||
)
|
||||
|
||||
return features
|
||||
|
||||
|
||||
class HansProcessor(DataProcessor):
|
||||
"""Processor for the HANS data set."""
|
||||
|
||||
def get_example_from_tensor_dict(self, tensor_dict):
|
||||
"""See base class."""
|
||||
return InputExample(
|
||||
tensor_dict["idx"].numpy(),
|
||||
tensor_dict["premise"].numpy().decode("utf-8"),
|
||||
tensor_dict["hypothesis"].numpy().decode("utf-8"),
|
||||
str(tensor_dict["label"].numpy()),
|
||||
)
|
||||
|
||||
def get_train_examples(self, data_dir):
|
||||
"""See base class."""
|
||||
return self._create_examples(self._read_tsv(os.path.join(data_dir, "heuristics_train_set.txt")), "train")
|
||||
|
||||
def get_dev_examples(self, data_dir):
|
||||
"""See base class."""
|
||||
return self._create_examples(self._read_tsv(os.path.join(data_dir, "heuristics_evaluation_set.txt")), "dev")
|
||||
|
||||
def get_labels(self):
|
||||
"""See base class."""
|
||||
return ["contradiction", "entailment", "neutral"]
|
||||
|
||||
def _create_examples(self, lines, set_type):
|
||||
"""Creates examples for the training and dev sets."""
|
||||
examples = []
|
||||
for (i, line) in enumerate(lines):
|
||||
if i == 0:
|
||||
continue
|
||||
guid = "%s-%s" % (set_type, line[0])
|
||||
text_a = line[5]
|
||||
text_b = line[6]
|
||||
pairID = line[7][2:] if line[7].startswith("ex") else line[7]
|
||||
label = line[-1]
|
||||
examples.append(InputExample(guid=guid, text_a=text_a, text_b=text_b, label=label, pairID=pairID))
|
||||
return examples
|
||||
|
||||
|
||||
glue_tasks_num_labels = {
|
||||
"hans": 3,
|
||||
}
|
||||
|
||||
glue_processors = {
|
||||
"hans": HansProcessor,
|
||||
}
|
||||
|
||||
glue_output_modes = {
|
||||
"hans": "classification",
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team.
|
||||
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
""" Finetuning the library models for sequence classification on HANS."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
from transformers import (
|
||||
AutoConfig,
|
||||
AutoModelForSequenceClassification,
|
||||
AutoTokenizer,
|
||||
HfArgumentParser,
|
||||
Trainer,
|
||||
TrainingArguments,
|
||||
default_data_collator,
|
||||
set_seed,
|
||||
)
|
||||
from utils_hans import HansDataset, InputFeatures, hans_processors
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModelArguments:
|
||||
"""
|
||||
Arguments pertaining to which model/config/tokenizer we are going to fine-tune from.
|
||||
"""
|
||||
|
||||
model_name_or_path: str = field(
|
||||
metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"}
|
||||
)
|
||||
config_name: Optional[str] = field(
|
||||
default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"}
|
||||
)
|
||||
tokenizer_name: Optional[str] = field(
|
||||
default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"}
|
||||
)
|
||||
cache_dir: Optional[str] = field(
|
||||
default=None, metadata={"help": "Where do you want to store the pretrained models downloaded from s3"}
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DataTrainingArguments:
|
||||
"""
|
||||
Arguments pertaining to what data we are going to input our model for training and eval.
|
||||
"""
|
||||
|
||||
task_name: str = field(
|
||||
metadata={"help": "The name of the task to train selected in the list: " + ", ".join(hans_processors.keys())}
|
||||
)
|
||||
data_dir: str = field(
|
||||
metadata={"help": "The input data dir. Should contain the .tsv files (or other data files) for the task."}
|
||||
)
|
||||
max_seq_length: int = field(
|
||||
default=128,
|
||||
metadata={
|
||||
"help": "The maximum total input sequence length after tokenization. Sequences longer "
|
||||
"than this will be truncated, sequences shorter will be padded."
|
||||
},
|
||||
)
|
||||
overwrite_cache: bool = field(
|
||||
default=False, metadata={"help": "Overwrite the cached training and evaluation sets"}
|
||||
)
|
||||
|
||||
|
||||
def hans_data_collator(features: List[InputFeatures]) -> Dict[str, torch.Tensor]:
|
||||
"""
|
||||
Data collator that removes the "pairID" key if present.
|
||||
"""
|
||||
batch = default_data_collator(features)
|
||||
_ = batch.pop("pairID", None)
|
||||
return batch
|
||||
|
||||
|
||||
def main():
|
||||
# See all possible arguments in src/transformers/training_args.py
|
||||
# or by passing the --help flag to this script.
|
||||
# We now keep distinct sets of args, for a cleaner separation of concerns.
|
||||
|
||||
parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments))
|
||||
model_args, data_args, training_args = parser.parse_args_into_dataclasses()
|
||||
|
||||
if (
|
||||
os.path.exists(training_args.output_dir)
|
||||
and os.listdir(training_args.output_dir)
|
||||
and training_args.do_train
|
||||
and not training_args.overwrite_output_dir
|
||||
):
|
||||
raise ValueError(
|
||||
f"Output directory ({training_args.output_dir}) already exists and is not empty. Use --overwrite_output_dir to overcome."
|
||||
)
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
|
||||
datefmt="%m/%d/%Y %H:%M:%S",
|
||||
level=logging.INFO if training_args.local_rank in [-1, 0] else logging.WARN,
|
||||
)
|
||||
logger.warning(
|
||||
"Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s",
|
||||
training_args.local_rank,
|
||||
training_args.device,
|
||||
training_args.n_gpu,
|
||||
bool(training_args.local_rank != -1),
|
||||
training_args.fp16,
|
||||
)
|
||||
logger.info("Training/evaluation parameters %s", training_args)
|
||||
|
||||
# Set seed
|
||||
set_seed(training_args.seed)
|
||||
|
||||
try:
|
||||
processor = hans_processors[data_args.task_name]()
|
||||
label_list = processor.get_labels()
|
||||
num_labels = len(label_list)
|
||||
except KeyError:
|
||||
raise ValueError("Task not found: %s" % (data_args.task_name))
|
||||
|
||||
# Load pretrained model and tokenizer
|
||||
#
|
||||
# Distributed training:
|
||||
# The .from_pretrained methods guarantee that only one local process can concurrently
|
||||
# download model & vocab.
|
||||
|
||||
config = AutoConfig.from_pretrained(
|
||||
model_args.config_name if model_args.config_name else model_args.model_name_or_path,
|
||||
num_labels=num_labels,
|
||||
finetuning_task=data_args.task_name,
|
||||
cache_dir=model_args.cache_dir,
|
||||
)
|
||||
tokenizer = AutoTokenizer.from_pretrained(
|
||||
model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path,
|
||||
cache_dir=model_args.cache_dir,
|
||||
)
|
||||
model = AutoModelForSequenceClassification.from_pretrained(
|
||||
model_args.model_name_or_path,
|
||||
from_tf=bool(".ckpt" in model_args.model_name_or_path),
|
||||
config=config,
|
||||
cache_dir=model_args.cache_dir,
|
||||
)
|
||||
|
||||
# Get datasets
|
||||
train_dataset = (
|
||||
HansDataset(
|
||||
data_dir=data_args.data_dir,
|
||||
tokenizer=tokenizer,
|
||||
task=data_args.task_name,
|
||||
max_seq_length=data_args.max_seq_length,
|
||||
overwrite_cache=data_args.overwrite_cache,
|
||||
)
|
||||
if training_args.do_train
|
||||
else None
|
||||
)
|
||||
eval_dataset = (
|
||||
HansDataset(
|
||||
data_dir=data_args.data_dir,
|
||||
tokenizer=tokenizer,
|
||||
task=data_args.task_name,
|
||||
max_seq_length=data_args.max_seq_length,
|
||||
overwrite_cache=data_args.overwrite_cache,
|
||||
evaluate=True,
|
||||
)
|
||||
if training_args.do_eval
|
||||
else None
|
||||
)
|
||||
|
||||
# Initialize our Trainer
|
||||
trainer = Trainer(
|
||||
model=model,
|
||||
args=training_args,
|
||||
train_dataset=train_dataset,
|
||||
eval_dataset=eval_dataset,
|
||||
data_collator=hans_data_collator,
|
||||
)
|
||||
|
||||
# Training
|
||||
if training_args.do_train:
|
||||
trainer.train(
|
||||
model_path=model_args.model_name_or_path if os.path.isdir(model_args.model_name_or_path) else None
|
||||
)
|
||||
trainer.save_model()
|
||||
# For convenience, we also re-save the tokenizer to the same directory,
|
||||
# so that you can share your model easily on huggingface.co/models =)
|
||||
if trainer.is_world_master():
|
||||
tokenizer.save_pretrained(training_args.output_dir)
|
||||
|
||||
# Evaluation
|
||||
if training_args.do_eval:
|
||||
logger.info("*** Evaluate ***")
|
||||
|
||||
output = trainer.predict(eval_dataset)
|
||||
preds = output.predictions
|
||||
preds = np.argmax(preds, axis=1)
|
||||
|
||||
pair_ids = [ex.pairID for ex in eval_dataset]
|
||||
output_eval_file = os.path.join(training_args.output_dir, "hans_predictions.txt")
|
||||
if trainer.is_world_master():
|
||||
with open(output_eval_file, "w") as writer:
|
||||
writer.write("pairID,gold_label\n")
|
||||
for pid, pred in zip(pair_ids, preds):
|
||||
writer.write("ex" + str(pid) + "," + label_list[int(pred)] + "\n")
|
||||
|
||||
trainer._log(output.metrics)
|
||||
|
||||
|
||||
def _mp_fn(index):
|
||||
# For xla_spawn (TPUs)
|
||||
main()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,636 +0,0 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team.
|
||||
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
""" Finetuning the library models for sequence classification on GLUE (Bert, XLM, XLNet, RoBERTa)."""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
from torch.utils.data import DataLoader, RandomSampler, SequentialSampler, TensorDataset
|
||||
from torch.utils.data.distributed import DistributedSampler
|
||||
from tqdm import tqdm, trange
|
||||
|
||||
from hans_processors import glue_output_modes as output_modes
|
||||
from hans_processors import glue_processors as processors
|
||||
from hans_processors import hans_convert_examples_to_features as convert_examples_to_features
|
||||
from transformers import (
|
||||
WEIGHTS_NAME,
|
||||
AdamW,
|
||||
AlbertConfig,
|
||||
AlbertForSequenceClassification,
|
||||
AlbertTokenizer,
|
||||
BertConfig,
|
||||
BertForSequenceClassification,
|
||||
BertTokenizer,
|
||||
DistilBertConfig,
|
||||
DistilBertForSequenceClassification,
|
||||
DistilBertTokenizer,
|
||||
RobertaConfig,
|
||||
RobertaForSequenceClassification,
|
||||
RobertaTokenizer,
|
||||
XLMConfig,
|
||||
XLMForSequenceClassification,
|
||||
XLMTokenizer,
|
||||
XLNetConfig,
|
||||
XLNetForSequenceClassification,
|
||||
XLNetTokenizer,
|
||||
get_linear_schedule_with_warmup,
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
from torch.utils.tensorboard import SummaryWriter
|
||||
except ImportError:
|
||||
from tensorboardX import SummaryWriter
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
MODEL_CLASSES = {
|
||||
"bert": (BertConfig, BertForSequenceClassification, BertTokenizer),
|
||||
"xlnet": (XLNetConfig, XLNetForSequenceClassification, XLNetTokenizer),
|
||||
"xlm": (XLMConfig, XLMForSequenceClassification, XLMTokenizer),
|
||||
"roberta": (RobertaConfig, RobertaForSequenceClassification, RobertaTokenizer),
|
||||
"distilbert": (DistilBertConfig, DistilBertForSequenceClassification, DistilBertTokenizer),
|
||||
"albert": (AlbertConfig, AlbertForSequenceClassification, AlbertTokenizer),
|
||||
}
|
||||
|
||||
|
||||
def set_seed(args):
|
||||
random.seed(args.seed)
|
||||
np.random.seed(args.seed)
|
||||
torch.manual_seed(args.seed)
|
||||
if args.n_gpu > 0:
|
||||
torch.cuda.manual_seed_all(args.seed)
|
||||
|
||||
|
||||
def train(args, train_dataset, model, tokenizer):
|
||||
""" Train the model """
|
||||
if args.local_rank in [-1, 0]:
|
||||
tb_writer = SummaryWriter()
|
||||
|
||||
args.train_batch_size = args.per_gpu_train_batch_size * max(1, args.n_gpu)
|
||||
train_sampler = RandomSampler(train_dataset) if args.local_rank == -1 else DistributedSampler(train_dataset)
|
||||
train_dataloader = DataLoader(train_dataset, sampler=train_sampler, batch_size=args.train_batch_size)
|
||||
|
||||
if args.max_steps > 0:
|
||||
t_total = args.max_steps
|
||||
args.num_train_epochs = args.max_steps // (len(train_dataloader) // args.gradient_accumulation_steps) + 1
|
||||
else:
|
||||
t_total = len(train_dataloader) // args.gradient_accumulation_steps * args.num_train_epochs
|
||||
|
||||
# Prepare optimizer and schedule (linear warmup and decay)
|
||||
no_decay = ["bias", "LayerNorm.weight"]
|
||||
optimizer_grouped_parameters = [
|
||||
{
|
||||
"params": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)],
|
||||
"weight_decay": args.weight_decay,
|
||||
},
|
||||
{"params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], "weight_decay": 0.0},
|
||||
]
|
||||
|
||||
optimizer = AdamW(optimizer_grouped_parameters, lr=args.learning_rate, eps=args.adam_epsilon)
|
||||
scheduler = get_linear_schedule_with_warmup(
|
||||
optimizer, num_warmup_steps=args.warmup_steps, num_training_steps=t_total
|
||||
)
|
||||
if args.fp16:
|
||||
try:
|
||||
from apex import amp
|
||||
except ImportError:
|
||||
raise ImportError("Please install apex from https://www.github.com/nvidia/apex to use fp16 training.")
|
||||
model, optimizer = amp.initialize(model, optimizer, opt_level=args.fp16_opt_level)
|
||||
|
||||
# multi-gpu training (should be after apex fp16 initialization)
|
||||
if args.n_gpu > 1:
|
||||
model = torch.nn.DataParallel(model)
|
||||
|
||||
# Distributed training (should be after apex fp16 initialization)
|
||||
if args.local_rank != -1:
|
||||
model = torch.nn.parallel.DistributedDataParallel(
|
||||
model, device_ids=[args.local_rank], output_device=args.local_rank, find_unused_parameters=True
|
||||
)
|
||||
|
||||
# Train!
|
||||
logger.info("***** Running training *****")
|
||||
logger.info(" Num examples = %d", len(train_dataset))
|
||||
logger.info(" Num Epochs = %d", args.num_train_epochs)
|
||||
logger.info(" Instantaneous batch size per GPU = %d", args.per_gpu_train_batch_size)
|
||||
logger.info(
|
||||
" Total train batch size (w. parallel, distributed & accumulation) = %d",
|
||||
args.train_batch_size
|
||||
* args.gradient_accumulation_steps
|
||||
* (torch.distributed.get_world_size() if args.local_rank != -1 else 1),
|
||||
)
|
||||
logger.info(" Gradient Accumulation steps = %d", args.gradient_accumulation_steps)
|
||||
logger.info(" Total optimization steps = %d", t_total)
|
||||
|
||||
global_step = 0
|
||||
tr_loss, logging_loss = 0.0, 0.0
|
||||
model.zero_grad()
|
||||
train_iterator = trange(int(args.num_train_epochs), desc="Epoch", disable=args.local_rank not in [-1, 0])
|
||||
set_seed(args) # Added here for reproductibility (even between python 2 and 3)
|
||||
for _ in train_iterator:
|
||||
epoch_iterator = tqdm(train_dataloader, desc="Iteration", disable=args.local_rank not in [-1, 0])
|
||||
for step, batch in enumerate(epoch_iterator):
|
||||
model.train()
|
||||
batch = tuple(t.to(args.device) for t in batch)
|
||||
inputs = {"input_ids": batch[0], "attention_mask": batch[1], "labels": batch[3]}
|
||||
if args.model_type != "distilbert":
|
||||
inputs["token_type_ids"] = (
|
||||
batch[2] if args.model_type in ["bert", "xlnet"] else None
|
||||
) # XLM, DistilBERT and RoBERTa don't use segment_ids
|
||||
outputs = model(**inputs)
|
||||
loss = outputs[0] # model outputs are always tuple in transformers (see doc)
|
||||
|
||||
if args.n_gpu > 1:
|
||||
loss = loss.mean() # mean() to average on multi-gpu parallel training
|
||||
if args.gradient_accumulation_steps > 1:
|
||||
loss = loss / args.gradient_accumulation_steps
|
||||
|
||||
if args.fp16:
|
||||
with amp.scale_loss(loss, optimizer) as scaled_loss:
|
||||
scaled_loss.backward()
|
||||
else:
|
||||
loss.backward()
|
||||
|
||||
tr_loss += loss.item()
|
||||
if (step + 1) % args.gradient_accumulation_steps == 0:
|
||||
if args.fp16:
|
||||
torch.nn.utils.clip_grad_norm_(amp.master_params(optimizer), args.max_grad_norm)
|
||||
else:
|
||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.max_grad_norm)
|
||||
|
||||
optimizer.step()
|
||||
scheduler.step() # Update learning rate schedule
|
||||
model.zero_grad()
|
||||
global_step += 1
|
||||
|
||||
if args.local_rank in [-1, 0] and args.logging_steps > 0 and global_step % args.logging_steps == 0:
|
||||
logs = {}
|
||||
if (
|
||||
args.local_rank == -1 and args.evaluate_during_training
|
||||
): # Only evaluate when single GPU otherwise metrics may not average well
|
||||
results = evaluate(args, model, tokenizer)
|
||||
for key, value in results.items():
|
||||
eval_key = "eval_{}".format(key)
|
||||
logs[eval_key] = value
|
||||
|
||||
loss_scalar = (tr_loss - logging_loss) / args.logging_steps
|
||||
learning_rate_scalar = scheduler.get_lr()[0]
|
||||
logs["learning_rate"] = learning_rate_scalar
|
||||
logs["loss"] = loss_scalar
|
||||
logging_loss = tr_loss
|
||||
|
||||
for key, value in logs.items():
|
||||
tb_writer.add_scalar(key, value, global_step)
|
||||
# print(json.dumps({**logs, **{'step': global_step}}))
|
||||
|
||||
if args.local_rank in [-1, 0] and args.save_steps > 0 and global_step % args.save_steps == 0:
|
||||
# Save model checkpoint
|
||||
output_dir = os.path.join(args.output_dir, "checkpoint-{}".format(global_step))
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
model_to_save = (
|
||||
model.module if hasattr(model, "module") else model
|
||||
) # Take care of distributed/parallel training
|
||||
model_to_save.save_pretrained(output_dir)
|
||||
torch.save(args, os.path.join(output_dir, "training_args.bin"))
|
||||
logger.info("Saving model checkpoint to %s", output_dir)
|
||||
|
||||
if args.max_steps > 0 and global_step > args.max_steps:
|
||||
epoch_iterator.close()
|
||||
break
|
||||
if args.max_steps > 0 and global_step > args.max_steps:
|
||||
train_iterator.close()
|
||||
break
|
||||
|
||||
if args.local_rank in [-1, 0]:
|
||||
tb_writer.close()
|
||||
|
||||
return global_step, tr_loss / global_step
|
||||
|
||||
|
||||
def evaluate(args, model, tokenizer, prefix=""):
|
||||
# Loop to handle MNLI double evaluation (matched, mis-matched)
|
||||
eval_task_names = ("mnli", "mnli-mm") if args.task_name == "mnli" else (args.task_name,)
|
||||
eval_outputs_dirs = (args.output_dir, args.output_dir + "-MM") if args.task_name == "mnli" else (args.output_dir,)
|
||||
|
||||
results = {}
|
||||
for eval_task, eval_output_dir in zip(eval_task_names, eval_outputs_dirs):
|
||||
eval_dataset, label_list = load_and_cache_examples(args, eval_task, tokenizer, evaluate=True)
|
||||
|
||||
if not os.path.exists(eval_output_dir) and args.local_rank in [-1, 0]:
|
||||
os.makedirs(eval_output_dir)
|
||||
|
||||
args.eval_batch_size = args.per_gpu_eval_batch_size * max(1, args.n_gpu)
|
||||
# Note that DistributedSampler samples randomly
|
||||
eval_sampler = SequentialSampler(eval_dataset)
|
||||
eval_dataloader = DataLoader(eval_dataset, sampler=eval_sampler, batch_size=args.eval_batch_size)
|
||||
|
||||
# multi-gpu eval
|
||||
if args.n_gpu > 1 and not isinstance(model, torch.nn.DataParallel):
|
||||
model = torch.nn.DataParallel(model)
|
||||
|
||||
# Eval!
|
||||
logger.info("***** Running evaluation {} *****".format(prefix))
|
||||
logger.info(" Num examples = %d", len(eval_dataset))
|
||||
logger.info(" Batch size = %d", args.eval_batch_size)
|
||||
eval_loss = 0.0
|
||||
nb_eval_steps = 0
|
||||
preds = None
|
||||
out_label_ids = None
|
||||
for batch in tqdm(eval_dataloader, desc="Evaluating"):
|
||||
model.eval()
|
||||
batch = tuple(t.to(args.device) for t in batch)
|
||||
|
||||
with torch.no_grad():
|
||||
inputs = {"input_ids": batch[0], "attention_mask": batch[1], "labels": batch[3]}
|
||||
if args.model_type != "distilbert":
|
||||
inputs["token_type_ids"] = (
|
||||
batch[2] if args.model_type in ["bert", "xlnet"] else None
|
||||
) # XLM, DistilBERT and RoBERTa don't use segment_ids
|
||||
outputs = model(**inputs)
|
||||
tmp_eval_loss, logits = outputs[:2]
|
||||
|
||||
eval_loss += tmp_eval_loss.mean().item()
|
||||
nb_eval_steps += 1
|
||||
if preds is None:
|
||||
preds = logits.detach().cpu().numpy()
|
||||
out_label_ids = inputs["labels"].detach().cpu().numpy()
|
||||
pair_ids = batch[4].detach().cpu().numpy()
|
||||
else:
|
||||
preds = np.append(preds, logits.detach().cpu().numpy(), axis=0)
|
||||
out_label_ids = np.append(out_label_ids, inputs["labels"].detach().cpu().numpy(), axis=0)
|
||||
pair_ids = np.append(pair_ids, batch[4].detach().cpu().numpy(), axis=0)
|
||||
|
||||
eval_loss = eval_loss / nb_eval_steps
|
||||
if args.output_mode == "classification":
|
||||
preds = np.argmax(preds, axis=1)
|
||||
elif args.output_mode == "regression":
|
||||
preds = np.squeeze(preds)
|
||||
|
||||
output_eval_file = os.path.join(eval_output_dir, "hans_predictions.txt")
|
||||
with open(output_eval_file, "w") as writer:
|
||||
writer.write("pairID,gld_label\n")
|
||||
for pid, pred in zip(pair_ids, preds):
|
||||
writer.write("ex" + str(pid) + "," + label_list[int(pred)] + "\n")
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def load_and_cache_examples(args, task, tokenizer, evaluate=False):
|
||||
if args.local_rank not in [-1, 0] and not evaluate:
|
||||
torch.distributed.barrier() # Make sure only the first process in distributed training process the dataset, and the others will use the cache
|
||||
|
||||
processor = processors[task]()
|
||||
output_mode = output_modes[task]
|
||||
# Load data features from cache or dataset file
|
||||
cached_features_file = os.path.join(
|
||||
args.data_dir,
|
||||
"cached_{}_{}_{}_{}".format(
|
||||
"dev" if evaluate else "train",
|
||||
list(filter(None, args.model_name_or_path.split("/"))).pop(),
|
||||
str(args.max_seq_length),
|
||||
str(task),
|
||||
),
|
||||
)
|
||||
|
||||
label_list = processor.get_labels()
|
||||
|
||||
if os.path.exists(cached_features_file) and not args.overwrite_cache:
|
||||
logger.info("Loading features from cached file %s", cached_features_file)
|
||||
features = torch.load(cached_features_file)
|
||||
else:
|
||||
logger.info("Creating features from dataset file at %s", args.data_dir)
|
||||
if task in ["mnli", "mnli-mm"] and args.model_type in ["roberta"]:
|
||||
# HACK(label indices are swapped in RoBERTa pretrained model)
|
||||
label_list[1], label_list[2] = label_list[2], label_list[1]
|
||||
examples = (
|
||||
processor.get_dev_examples(args.data_dir) if evaluate else processor.get_train_examples(args.data_dir)
|
||||
)
|
||||
features = convert_examples_to_features(
|
||||
examples,
|
||||
tokenizer,
|
||||
label_list=label_list,
|
||||
max_length=args.max_seq_length,
|
||||
output_mode=output_mode,
|
||||
pad_on_left=bool(args.model_type in ["xlnet"]), # pad on the left for xlnet
|
||||
pad_token=tokenizer.pad_token_id,
|
||||
pad_token_segment_id=tokenizer.pad_token_type_id,
|
||||
)
|
||||
if args.local_rank in [-1, 0]:
|
||||
logger.info("Saving features into cached file %s", cached_features_file)
|
||||
torch.save(features, cached_features_file)
|
||||
|
||||
if args.local_rank == 0 and not evaluate:
|
||||
torch.distributed.barrier() # Make sure only the first process in distributed training process the dataset, and the others will use the cache
|
||||
|
||||
# Convert to Tensors and build dataset
|
||||
all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long)
|
||||
all_attention_mask = torch.tensor([f.attention_mask for f in features], dtype=torch.long)
|
||||
all_token_type_ids = torch.tensor([f.token_type_ids for f in features], dtype=torch.long)
|
||||
if output_mode == "classification":
|
||||
all_labels = torch.tensor([f.label for f in features], dtype=torch.long)
|
||||
elif output_mode == "regression":
|
||||
all_labels = torch.tensor([f.label for f in features], dtype=torch.float)
|
||||
all_pair_ids = torch.tensor([int(f.pairID) for f in features], dtype=torch.long)
|
||||
|
||||
dataset = TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_labels, all_pair_ids)
|
||||
return dataset, label_list
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
# Required parameters
|
||||
parser.add_argument(
|
||||
"--data_dir",
|
||||
default=None,
|
||||
type=str,
|
||||
required=True,
|
||||
help="The input data dir. Should contain the .tsv files (or other data files) for the task.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model_type",
|
||||
default=None,
|
||||
type=str,
|
||||
required=True,
|
||||
help="Model type selected in the list: " + ", ".join(MODEL_CLASSES.keys()),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model_name_or_path",
|
||||
default=None,
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to pretrained model or model identifier from huggingface.co/models",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--task_name",
|
||||
default=None,
|
||||
type=str,
|
||||
required=True,
|
||||
help="The name of the task to train selected in the list: " + ", ".join(processors.keys()),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output_dir",
|
||||
default=None,
|
||||
type=str,
|
||||
required=True,
|
||||
help="The output directory where the model predictions and checkpoints will be written.",
|
||||
)
|
||||
|
||||
# Other parameters
|
||||
parser.add_argument(
|
||||
"--config_name", default="", type=str, help="Pretrained config name or path if not the same as model_name"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tokenizer_name",
|
||||
default="",
|
||||
type=str,
|
||||
help="Pretrained tokenizer name or path if not the same as model_name",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cache_dir",
|
||||
default="",
|
||||
type=str,
|
||||
help="Where do you want to store the pre-trained models downloaded from s3",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max_seq_length",
|
||||
default=128,
|
||||
type=int,
|
||||
help="The maximum total input sequence length after tokenization. Sequences longer "
|
||||
"than this will be truncated, sequences shorter will be padded.",
|
||||
)
|
||||
parser.add_argument("--do_train", action="store_true", help="Whether to run training.")
|
||||
parser.add_argument("--do_eval", action="store_true", help="Whether to run eval on the dev set.")
|
||||
parser.add_argument(
|
||||
"--evaluate_during_training", action="store_true", help="Rul evaluation during training at each logging step."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--do_lower_case", action="store_true", help="Set this flag if you are using an uncased model."
|
||||
)
|
||||
|
||||
parser.add_argument("--per_gpu_train_batch_size", default=8, type=int, help="Batch size per GPU/CPU for training.")
|
||||
parser.add_argument(
|
||||
"--per_gpu_eval_batch_size", default=8, type=int, help="Batch size per GPU/CPU for evaluation."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--gradient_accumulation_steps",
|
||||
type=int,
|
||||
default=1,
|
||||
help="Number of updates steps to accumulate before performing a backward/update pass.",
|
||||
)
|
||||
parser.add_argument("--learning_rate", default=5e-5, type=float, help="The initial learning rate for Adam.")
|
||||
parser.add_argument("--weight_decay", default=0.0, type=float, help="Weight decay if we apply some.")
|
||||
parser.add_argument("--adam_epsilon", default=1e-8, type=float, help="Epsilon for Adam optimizer.")
|
||||
parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.")
|
||||
parser.add_argument(
|
||||
"--num_train_epochs", default=3.0, type=float, help="Total number of training epochs to perform."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max_steps",
|
||||
default=-1,
|
||||
type=int,
|
||||
help="If > 0: set total number of training steps to perform. Override num_train_epochs.",
|
||||
)
|
||||
parser.add_argument("--warmup_steps", default=0, type=int, help="Linear warmup over warmup_steps.")
|
||||
|
||||
parser.add_argument("--logging_steps", type=int, default=50, help="Log every X updates steps.")
|
||||
parser.add_argument("--save_steps", type=int, default=50, help="Save checkpoint every X updates steps.")
|
||||
parser.add_argument(
|
||||
"--eval_all_checkpoints",
|
||||
action="store_true",
|
||||
help="Evaluate all checkpoints starting with the same prefix as model_name ending and ending with step number",
|
||||
)
|
||||
parser.add_argument("--no_cuda", action="store_true", help="Avoid using CUDA when available")
|
||||
parser.add_argument(
|
||||
"--overwrite_output_dir", action="store_true", help="Overwrite the content of the output directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--overwrite_cache", action="store_true", help="Overwrite the cached training and evaluation sets"
|
||||
)
|
||||
parser.add_argument("--seed", type=int, default=42, help="random seed for initialization")
|
||||
|
||||
parser.add_argument(
|
||||
"--fp16",
|
||||
action="store_true",
|
||||
help="Whether to use 16-bit (mixed) precision (through NVIDIA apex) instead of 32-bit",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--fp16_opt_level",
|
||||
type=str,
|
||||
default="O1",
|
||||
help="For fp16: Apex AMP optimization level selected in ['O0', 'O1', 'O2', and 'O3']."
|
||||
"See details at https://nvidia.github.io/apex/amp.html",
|
||||
)
|
||||
parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank")
|
||||
parser.add_argument("--server_ip", type=str, default="", help="For distant debugging.")
|
||||
parser.add_argument("--server_port", type=str, default="", help="For distant debugging.")
|
||||
args = parser.parse_args()
|
||||
|
||||
if (
|
||||
os.path.exists(args.output_dir)
|
||||
and os.listdir(args.output_dir)
|
||||
and args.do_train
|
||||
and not args.overwrite_output_dir
|
||||
):
|
||||
raise ValueError(
|
||||
"Output directory ({}) already exists and is not empty. Use --overwrite_output_dir to overcome.".format(
|
||||
args.output_dir
|
||||
)
|
||||
)
|
||||
|
||||
# Setup distant debugging if needed
|
||||
if args.server_ip and args.server_port:
|
||||
# Distant debugging - see https://code.visualstudio.com/docs/python/debugging#_attach-to-a-local-script
|
||||
import ptvsd
|
||||
|
||||
print("Waiting for debugger attach")
|
||||
ptvsd.enable_attach(address=(args.server_ip, args.server_port), redirect_output=True)
|
||||
ptvsd.wait_for_attach()
|
||||
|
||||
# Setup CUDA, GPU & distributed training
|
||||
if args.local_rank == -1 or args.no_cuda:
|
||||
device = torch.device("cuda" if torch.cuda.is_available() and not args.no_cuda else "cpu")
|
||||
args.n_gpu = 0 if args.no_cuda else torch.cuda.device_count()
|
||||
else: # Initializes the distributed backend which will take care of sychronizing nodes/GPUs
|
||||
torch.cuda.set_device(args.local_rank)
|
||||
device = torch.device("cuda", args.local_rank)
|
||||
torch.distributed.init_process_group(backend="nccl")
|
||||
args.n_gpu = 1
|
||||
args.device = device
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
|
||||
datefmt="%m/%d/%Y %H:%M:%S",
|
||||
level=logging.INFO if args.local_rank in [-1, 0] else logging.WARN,
|
||||
)
|
||||
logger.warning(
|
||||
"Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s",
|
||||
args.local_rank,
|
||||
device,
|
||||
args.n_gpu,
|
||||
bool(args.local_rank != -1),
|
||||
args.fp16,
|
||||
)
|
||||
|
||||
# Set seed
|
||||
set_seed(args)
|
||||
|
||||
# Prepare GLUE task
|
||||
args.task_name = args.task_name.lower()
|
||||
if args.task_name not in processors:
|
||||
raise ValueError("Task not found: %s" % (args.task_name))
|
||||
processor = processors[args.task_name]()
|
||||
args.output_mode = output_modes[args.task_name]
|
||||
label_list = processor.get_labels()
|
||||
num_labels = len(label_list)
|
||||
|
||||
# Load pretrained model and tokenizer
|
||||
if args.local_rank not in [-1, 0]:
|
||||
torch.distributed.barrier() # Make sure only the first process in distributed training will download model & vocab
|
||||
|
||||
args.model_type = args.model_type.lower()
|
||||
config_class, model_class, tokenizer_class = MODEL_CLASSES[args.model_type]
|
||||
config = config_class.from_pretrained(
|
||||
args.config_name if args.config_name else args.model_name_or_path,
|
||||
num_labels=num_labels,
|
||||
finetuning_task=args.task_name,
|
||||
cache_dir=args.cache_dir if args.cache_dir else None,
|
||||
)
|
||||
tokenizer = tokenizer_class.from_pretrained(
|
||||
args.tokenizer_name if args.tokenizer_name else args.model_name_or_path,
|
||||
do_lower_case=args.do_lower_case,
|
||||
cache_dir=args.cache_dir if args.cache_dir else None,
|
||||
)
|
||||
model = model_class.from_pretrained(
|
||||
args.model_name_or_path,
|
||||
from_tf=bool(".ckpt" in args.model_name_or_path),
|
||||
config=config,
|
||||
cache_dir=args.cache_dir if args.cache_dir else None,
|
||||
)
|
||||
|
||||
if args.local_rank == 0:
|
||||
torch.distributed.barrier() # Make sure only the first process in distributed training will download model & vocab
|
||||
|
||||
model.to(args.device)
|
||||
|
||||
logger.info("Training/evaluation parameters %s", args)
|
||||
|
||||
# Training
|
||||
if args.do_train:
|
||||
train_dataset, _ = load_and_cache_examples(args, args.task_name, tokenizer, evaluate=False)
|
||||
global_step, tr_loss = train(args, train_dataset, model, tokenizer)
|
||||
logger.info(" global_step = %s, average loss = %s", global_step, tr_loss)
|
||||
|
||||
# Saving best-practices: if you use defaults names for the model, you can reload it using from_pretrained()
|
||||
if args.do_train and (args.local_rank == -1 or torch.distributed.get_rank() == 0):
|
||||
# Create output directory if needed
|
||||
if not os.path.exists(args.output_dir) and args.local_rank in [-1, 0]:
|
||||
os.makedirs(args.output_dir)
|
||||
|
||||
logger.info("Saving model checkpoint to %s", args.output_dir)
|
||||
# Save a trained model, configuration and tokenizer using `save_pretrained()`.
|
||||
# They can then be reloaded using `from_pretrained()`
|
||||
model_to_save = (
|
||||
model.module if hasattr(model, "module") else model
|
||||
) # Take care of distributed/parallel training
|
||||
model_to_save.save_pretrained(args.output_dir)
|
||||
tokenizer.save_pretrained(args.output_dir)
|
||||
|
||||
# Good practice: save your training arguments together with the trained model
|
||||
torch.save(args, os.path.join(args.output_dir, "training_args.bin"))
|
||||
|
||||
# Load a trained model and vocabulary that you have fine-tuned
|
||||
model = model_class.from_pretrained(args.output_dir)
|
||||
tokenizer = tokenizer_class.from_pretrained(args.output_dir)
|
||||
model.to(args.device)
|
||||
|
||||
# Evaluation
|
||||
results = {}
|
||||
if args.do_eval and args.local_rank in [-1, 0]:
|
||||
tokenizer = tokenizer_class.from_pretrained(args.output_dir, do_lower_case=args.do_lower_case)
|
||||
checkpoints = [args.output_dir]
|
||||
if args.eval_all_checkpoints:
|
||||
checkpoints = list(
|
||||
os.path.dirname(c) for c in sorted(glob.glob(args.output_dir + "/**/" + WEIGHTS_NAME, recursive=True))
|
||||
)
|
||||
logging.getLogger("transformers.modeling_utils").setLevel(logging.WARN) # Reduce logging
|
||||
logger.info("Evaluate the following checkpoints: %s", checkpoints)
|
||||
for checkpoint in checkpoints:
|
||||
global_step = checkpoint.split("-")[-1] if len(checkpoints) > 1 else ""
|
||||
prefix = checkpoint.split("/")[-1] if checkpoint.find("checkpoint") != -1 else ""
|
||||
|
||||
model = model_class.from_pretrained(checkpoint)
|
||||
model.to(args.device)
|
||||
result = evaluate(args, model, tokenizer, prefix=prefix)
|
||||
result = dict((k + "_{}".format(global_step), v) for k, v in result.items())
|
||||
results.update(result)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -14,108 +14,281 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import copy
|
||||
import csv
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import tqdm
|
||||
from filelock import FileLock
|
||||
|
||||
from transformers import DataProcessor, PreTrainedTokenizer, is_tf_available, is_torch_available
|
||||
|
||||
|
||||
class InputExample(object):
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class InputExample:
|
||||
"""
|
||||
A single training/test example for simple sequence classification.
|
||||
|
||||
Args:
|
||||
guid: Unique id for the example.
|
||||
text_a: string. The untokenized text of the first sequence. For single
|
||||
sequence tasks, only this sequence must be specified.
|
||||
sequence tasks, only this sequence must be specified.
|
||||
text_b: (Optional) string. The untokenized text of the second sequence.
|
||||
Only must be specified for sequence pair tasks.
|
||||
Only must be specified for sequence pair tasks.
|
||||
label: (Optional) string. The label of the example. This should be
|
||||
specified for train and dev examples, but not for test examples.
|
||||
specified for train and dev examples, but not for test examples.
|
||||
pairID: (Optional) string. Unique identifier for the pair of sentences.
|
||||
"""
|
||||
|
||||
def __init__(self, guid, text_a, text_b=None, label=None, pairID=None):
|
||||
self.guid = guid
|
||||
self.text_a = text_a
|
||||
self.text_b = text_b
|
||||
self.label = label
|
||||
self.pairID = pairID
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.to_json_string())
|
||||
|
||||
def to_dict(self):
|
||||
"""Serializes this instance to a Python dictionary."""
|
||||
output = copy.deepcopy(self.__dict__)
|
||||
return output
|
||||
|
||||
def to_json_string(self):
|
||||
"""Serializes this instance to a JSON string."""
|
||||
return json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n"
|
||||
guid: str
|
||||
text_a: str
|
||||
text_b: Optional[str] = None
|
||||
label: Optional[str] = None
|
||||
pairID: Optional[str] = None
|
||||
|
||||
|
||||
class InputFeatures(object):
|
||||
@dataclass(frozen=True)
|
||||
class InputFeatures:
|
||||
"""
|
||||
A single set of features of data.
|
||||
Property names are the same names as the corresponding inputs to a model.
|
||||
|
||||
Args:
|
||||
input_ids: Indices of input sequence tokens in the vocabulary.
|
||||
attention_mask: Mask to avoid performing attention on padding token indices.
|
||||
Mask values selected in ``[0, 1]``:
|
||||
Usually ``1`` for tokens that are NOT MASKED, ``0`` for MASKED (padded) tokens.
|
||||
token_type_ids: Segment token indices to indicate first and second portions of the inputs.
|
||||
label: Label corresponding to the input
|
||||
token_type_ids: (Optional) Segment token indices to indicate first and second
|
||||
portions of the inputs. Only some models use them.
|
||||
label: (Optional) Label corresponding to the input. Int for classification problems,
|
||||
float for regression problems.
|
||||
pairID: (Optional) Unique identifier for the pair of sentences.
|
||||
"""
|
||||
|
||||
def __init__(self, input_ids, attention_mask, token_type_ids, label, pairID=None):
|
||||
self.input_ids = input_ids
|
||||
self.attention_mask = attention_mask
|
||||
self.token_type_ids = token_type_ids
|
||||
self.label = label
|
||||
self.pairID = pairID
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.to_json_string())
|
||||
|
||||
def to_dict(self):
|
||||
"""Serializes this instance to a Python dictionary."""
|
||||
output = copy.deepcopy(self.__dict__)
|
||||
return output
|
||||
|
||||
def to_json_string(self):
|
||||
"""Serializes this instance to a JSON string."""
|
||||
return json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n"
|
||||
input_ids: List[int]
|
||||
attention_mask: Optional[List[int]] = None
|
||||
token_type_ids: Optional[List[int]] = None
|
||||
label: Optional[Union[int, float]] = None
|
||||
pairID: Optional[int] = None
|
||||
|
||||
|
||||
class DataProcessor(object):
|
||||
"""Base class for data converters for sequence classification data sets."""
|
||||
if is_torch_available():
|
||||
import torch
|
||||
from torch.utils.data.dataset import Dataset
|
||||
|
||||
def get_example_from_tensor_dict(self, tensor_dict):
|
||||
"""Gets an example from a dict with tensorflow tensors
|
||||
|
||||
Args:
|
||||
tensor_dict: Keys and values should match the corresponding Glue
|
||||
tensorflow_dataset examples.
|
||||
class HansDataset(Dataset):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
This will be superseded by a framework-agnostic approach
|
||||
soon.
|
||||
"""
|
||||
|
||||
features: List[InputFeatures]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data_dir: str,
|
||||
tokenizer: PreTrainedTokenizer,
|
||||
task: str,
|
||||
max_seq_length: Optional[int] = None,
|
||||
overwrite_cache=False,
|
||||
evaluate: bool = False,
|
||||
):
|
||||
processor = hans_processors[task]()
|
||||
|
||||
cached_features_file = os.path.join(
|
||||
data_dir,
|
||||
"cached_{}_{}_{}_{}".format(
|
||||
"dev" if evaluate else "train", tokenizer.__class__.__name__, str(max_seq_length), task,
|
||||
),
|
||||
)
|
||||
|
||||
# Make sure only the first process in distributed training processes the dataset,
|
||||
# and the others will use the cache.
|
||||
lock_path = cached_features_file + ".lock"
|
||||
with FileLock(lock_path):
|
||||
|
||||
if os.path.exists(cached_features_file) and not overwrite_cache:
|
||||
logger.info(f"Loading features from cached file {cached_features_file}")
|
||||
self.features = torch.load(cached_features_file)
|
||||
else:
|
||||
logger.info(f"Creating features from dataset file at {data_dir}")
|
||||
label_list = processor.get_labels()
|
||||
|
||||
examples = (
|
||||
processor.get_dev_examples(data_dir) if evaluate else processor.get_train_examples(data_dir)
|
||||
)
|
||||
|
||||
logger.info("Training examples: %s", len(examples))
|
||||
self.features = hans_convert_examples_to_features(examples, label_list, max_seq_length, tokenizer)
|
||||
logger.info("Saving features into cached file %s", cached_features_file)
|
||||
torch.save(self.features, cached_features_file)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.features)
|
||||
|
||||
def __getitem__(self, i) -> InputFeatures:
|
||||
return self.features[i]
|
||||
|
||||
|
||||
if is_tf_available():
|
||||
import tensorflow as tf
|
||||
|
||||
class TFHansDataset:
|
||||
"""
|
||||
This will be superseded by a framework-agnostic approach
|
||||
soon.
|
||||
"""
|
||||
|
||||
features: List[InputFeatures]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data_dir: str,
|
||||
tokenizer: PreTrainedTokenizer,
|
||||
task: str,
|
||||
max_seq_length: Optional[int] = 128,
|
||||
overwrite_cache=False,
|
||||
evaluate: bool = False,
|
||||
):
|
||||
processor = hans_processors[task]()
|
||||
label_list = processor.get_labels()
|
||||
|
||||
examples = processor.get_dev_examples(data_dir) if evaluate else processor.get_train_examples(data_dir)
|
||||
self.features = hans_convert_examples_to_features(examples, label_list, max_seq_length, tokenizer)
|
||||
|
||||
def gen():
|
||||
for (ex_index, ex) in tqdm.tqdm(enumerate(self.features), desc="convert examples to features"):
|
||||
if ex_index % 10000 == 0:
|
||||
logger.info("Writing example %d of %d" % (ex_index, len(examples)))
|
||||
|
||||
yield (
|
||||
{
|
||||
"example_id": 0,
|
||||
"input_ids": ex.input_ids,
|
||||
"attention_mask": ex.attention_mask,
|
||||
"token_type_ids": ex.token_type_ids,
|
||||
},
|
||||
ex.label,
|
||||
)
|
||||
|
||||
self.dataset = tf.data.Dataset.from_generator(
|
||||
gen,
|
||||
(
|
||||
{
|
||||
"example_id": tf.int32,
|
||||
"input_ids": tf.int32,
|
||||
"attention_mask": tf.int32,
|
||||
"token_type_ids": tf.int32,
|
||||
},
|
||||
tf.int64,
|
||||
),
|
||||
(
|
||||
{
|
||||
"example_id": tf.TensorShape([]),
|
||||
"input_ids": tf.TensorShape([None, None]),
|
||||
"attention_mask": tf.TensorShape([None, None]),
|
||||
"token_type_ids": tf.TensorShape([None, None]),
|
||||
},
|
||||
tf.TensorShape([]),
|
||||
),
|
||||
)
|
||||
|
||||
def get_dataset(self):
|
||||
return self.dataset
|
||||
|
||||
def __len__(self):
|
||||
return len(self.features)
|
||||
|
||||
def __getitem__(self, i) -> InputFeatures:
|
||||
return self.features[i]
|
||||
|
||||
|
||||
class HansProcessor(DataProcessor):
|
||||
"""Processor for the HANS data set."""
|
||||
|
||||
def get_train_examples(self, data_dir):
|
||||
"""Gets a collection of `InputExample`s for the train set."""
|
||||
raise NotImplementedError()
|
||||
"""See base class."""
|
||||
return self._create_examples(self._read_tsv(os.path.join(data_dir, "heuristics_train_set.txt")), "train")
|
||||
|
||||
def get_dev_examples(self, data_dir):
|
||||
"""Gets a collection of `InputExample`s for the dev set."""
|
||||
raise NotImplementedError()
|
||||
"""See base class."""
|
||||
return self._create_examples(self._read_tsv(os.path.join(data_dir, "heuristics_evaluation_set.txt")), "dev")
|
||||
|
||||
def get_labels(self):
|
||||
"""Gets the list of labels for this data set."""
|
||||
raise NotImplementedError()
|
||||
"""See base class."""
|
||||
return ["contradiction", "entailment", "neutral"]
|
||||
|
||||
@classmethod
|
||||
def _read_tsv(cls, input_file, quotechar=None):
|
||||
"""Reads a tab separated value file."""
|
||||
with open(input_file, "r", encoding="utf-8-sig") as f:
|
||||
reader = csv.reader(f, delimiter="\t", quotechar=quotechar)
|
||||
lines = []
|
||||
for line in reader:
|
||||
lines.append(line)
|
||||
return lines
|
||||
def _create_examples(self, lines, set_type):
|
||||
"""Creates examples for the training and dev sets."""
|
||||
examples = []
|
||||
for (i, line) in enumerate(lines):
|
||||
if i == 0:
|
||||
continue
|
||||
guid = "%s-%s" % (set_type, line[0])
|
||||
text_a = line[5]
|
||||
text_b = line[6]
|
||||
pairID = line[7][2:] if line[7].startswith("ex") else line[7]
|
||||
label = line[-1]
|
||||
examples.append(InputExample(guid=guid, text_a=text_a, text_b=text_b, label=label, pairID=pairID))
|
||||
return examples
|
||||
|
||||
|
||||
def hans_convert_examples_to_features(
|
||||
examples: List[InputExample], label_list: List[str], max_length: int, tokenizer: PreTrainedTokenizer,
|
||||
):
|
||||
"""
|
||||
Loads a data file into a list of ``InputFeatures``
|
||||
|
||||
Args:
|
||||
examples: List of ``InputExamples`` containing the examples.
|
||||
tokenizer: Instance of a tokenizer that will tokenize the examples.
|
||||
max_length: Maximum example length.
|
||||
label_list: List of labels. Can be obtained from the processor using the ``processor.get_labels()`` method.
|
||||
output_mode: String indicating the output mode. Either ``regression`` or ``classification``.
|
||||
|
||||
Returns:
|
||||
A list of task-specific ``InputFeatures`` which can be fed to the model.
|
||||
|
||||
"""
|
||||
|
||||
label_map = {label: i for i, label in enumerate(label_list)}
|
||||
|
||||
features = []
|
||||
for (ex_index, example) in tqdm.tqdm(enumerate(examples), desc="convert examples to features"):
|
||||
if ex_index % 10000 == 0:
|
||||
logger.info("Writing example %d" % (ex_index))
|
||||
|
||||
inputs = tokenizer.encode_plus(
|
||||
example.text_a,
|
||||
example.text_b,
|
||||
add_special_tokens=True,
|
||||
max_length=max_length,
|
||||
pad_to_max_length=True,
|
||||
return_overflowing_tokens=True,
|
||||
)
|
||||
|
||||
label = label_map[example.label] if example.label in label_map else 0
|
||||
|
||||
pairID = int(example.pairID)
|
||||
|
||||
features.append(InputFeatures(**inputs, label=label, pairID=pairID))
|
||||
|
||||
for i, example in enumerate(examples[:5]):
|
||||
logger.info("*** Example ***")
|
||||
logger.info(f"guid: {example}")
|
||||
logger.info(f"features: {features[i]}")
|
||||
|
||||
return features
|
||||
|
||||
|
||||
hans_tasks_num_labels = {
|
||||
"hans": 3,
|
||||
}
|
||||
|
||||
hans_processors = {
|
||||
"hans": HansProcessor,
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2018 The HuggingFace Inc. team.
|
||||
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
""" Benchmarking the library on inference and training in Tensorflow"""
|
||||
|
||||
from transformers import HfArgumentParser, TensorflowBenchmark, TensorflowBenchmarkArguments
|
||||
|
||||
|
||||
def main():
|
||||
parser = HfArgumentParser(TensorflowBenchmarkArguments)
|
||||
benchmark_args = parser.parse_args_into_dataclasses()[0]
|
||||
benchmark = TensorflowBenchmark(args=benchmark_args)
|
||||
benchmark.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -34,8 +34,8 @@ from transformers import (
|
||||
AutoConfig,
|
||||
AutoModelForSequenceClassification,
|
||||
AutoTokenizer,
|
||||
DefaultDataCollator,
|
||||
GlueDataset,
|
||||
default_data_collator,
|
||||
glue_compute_metrics,
|
||||
glue_output_modes,
|
||||
glue_processors,
|
||||
@@ -424,7 +424,7 @@ def main():
|
||||
eval_dataset = Subset(eval_dataset, list(range(min(args.data_subset, len(eval_dataset)))))
|
||||
eval_sampler = SequentialSampler(eval_dataset) if args.local_rank == -1 else DistributedSampler(eval_dataset)
|
||||
eval_dataloader = DataLoader(
|
||||
eval_dataset, sampler=eval_sampler, batch_size=args.batch_size, collate_fn=DefaultDataCollator().collate_batch
|
||||
eval_dataset, sampler=eval_sampler, batch_size=args.batch_size, collate_fn=default_data_collator
|
||||
)
|
||||
|
||||
# Compute head entropy and importance score
|
||||
|
||||
+105
-43
@@ -2,6 +2,8 @@ import argparse
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
import numpy as np
|
||||
import pytorch_lightning as pl
|
||||
@@ -13,10 +15,13 @@ from transformers import (
|
||||
AutoModel,
|
||||
AutoModelForPreTraining,
|
||||
AutoModelForQuestionAnswering,
|
||||
AutoModelForSeq2SeqLM,
|
||||
AutoModelForSequenceClassification,
|
||||
AutoModelForTokenClassification,
|
||||
AutoModelWithLMHead,
|
||||
AutoTokenizer,
|
||||
PretrainedConfig,
|
||||
PreTrainedTokenizer,
|
||||
get_linear_schedule_with_warmup,
|
||||
)
|
||||
|
||||
@@ -31,6 +36,8 @@ MODEL_MODES = {
|
||||
"pretraining": AutoModelForPreTraining,
|
||||
"token-classification": AutoModelForTokenClassification,
|
||||
"language-modeling": AutoModelWithLMHead,
|
||||
"summarization": AutoModelForSeq2SeqLM,
|
||||
"translation": AutoModelForSeq2SeqLM,
|
||||
}
|
||||
|
||||
|
||||
@@ -38,33 +45,59 @@ def set_seed(args: argparse.Namespace):
|
||||
random.seed(args.seed)
|
||||
np.random.seed(args.seed)
|
||||
torch.manual_seed(args.seed)
|
||||
if args.n_gpu > 0:
|
||||
if args.gpus > 0:
|
||||
torch.cuda.manual_seed_all(args.seed)
|
||||
|
||||
|
||||
class BaseTransformer(pl.LightningModule):
|
||||
def __init__(self, hparams: argparse.Namespace, num_labels=None, mode="base", **config_kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
hparams: argparse.Namespace,
|
||||
num_labels=None,
|
||||
mode="base",
|
||||
config=None,
|
||||
tokenizer=None,
|
||||
model=None,
|
||||
**config_kwargs
|
||||
):
|
||||
"Initialize a model."
|
||||
|
||||
super().__init__()
|
||||
self.hparams = hparams
|
||||
self.step_count = 0
|
||||
self.tfmr_ckpts = {}
|
||||
self.output_dir = Path(self.hparams.output_dir)
|
||||
cache_dir = self.hparams.cache_dir if self.hparams.cache_dir else None
|
||||
self.config = AutoConfig.from_pretrained(
|
||||
self.hparams.config_name if self.hparams.config_name else self.hparams.model_name_or_path,
|
||||
**({"num_labels": num_labels} if num_labels is not None else {}),
|
||||
cache_dir=cache_dir,
|
||||
**config_kwargs,
|
||||
)
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(
|
||||
self.hparams.tokenizer_name if self.hparams.tokenizer_name else self.hparams.model_name_or_path,
|
||||
cache_dir=cache_dir,
|
||||
)
|
||||
self.model = MODEL_MODES[mode].from_pretrained(
|
||||
self.hparams.model_name_or_path,
|
||||
from_tf=bool(".ckpt" in self.hparams.model_name_or_path),
|
||||
config=self.config,
|
||||
cache_dir=cache_dir,
|
||||
)
|
||||
if config is None:
|
||||
self.config = AutoConfig.from_pretrained(
|
||||
self.hparams.config_name if self.hparams.config_name else self.hparams.model_name_or_path,
|
||||
**({"num_labels": num_labels} if num_labels is not None else {}),
|
||||
cache_dir=cache_dir,
|
||||
**config_kwargs,
|
||||
)
|
||||
else:
|
||||
self.config: PretrainedConfig = config
|
||||
if tokenizer is None:
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(
|
||||
self.hparams.tokenizer_name if self.hparams.tokenizer_name else self.hparams.model_name_or_path,
|
||||
cache_dir=cache_dir,
|
||||
)
|
||||
else:
|
||||
self.tokenizer: PreTrainedTokenizer = tokenizer
|
||||
if model is None:
|
||||
self.model_type = MODEL_MODES[mode]
|
||||
self.model = self.model_type.from_pretrained(
|
||||
self.hparams.model_name_or_path,
|
||||
from_tf=bool(".ckpt" in self.hparams.model_name_or_path),
|
||||
config=self.config,
|
||||
cache_dir=cache_dir,
|
||||
)
|
||||
else:
|
||||
self.model_type = None
|
||||
self.model = model
|
||||
|
||||
def load_hf_checkpoint(self, *args, **kwargs):
|
||||
self.model = self.model_type.from_pretrained(*args, **kwargs)
|
||||
|
||||
def is_logger(self):
|
||||
return self.trainer.proc_rank <= 0
|
||||
@@ -138,6 +171,15 @@ class BaseTransformer(pl.LightningModule):
|
||||
),
|
||||
)
|
||||
|
||||
@pl.utilities.rank_zero_only
|
||||
def on_save_checkpoint(self, checkpoint: Dict[str, Any]) -> None:
|
||||
save_path = self.output_dir.joinpath("best_tfmr")
|
||||
save_path.mkdir(exist_ok=True)
|
||||
self.model.config.save_step = self.step_count
|
||||
self.model.save_pretrained(save_path)
|
||||
self.tokenizer.save_pretrained(save_path)
|
||||
self.tfmr_ckpts[self.step_count] = save_path
|
||||
|
||||
@staticmethod
|
||||
def add_model_specific_args(parser, root_dir):
|
||||
parser.add_argument(
|
||||
@@ -152,7 +194,7 @@ class BaseTransformer(pl.LightningModule):
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tokenizer_name",
|
||||
default="",
|
||||
default=None,
|
||||
type=str,
|
||||
help="Pretrained tokenizer name or path if not the same as model_name",
|
||||
)
|
||||
@@ -165,7 +207,7 @@ class BaseTransformer(pl.LightningModule):
|
||||
parser.add_argument("--learning_rate", default=5e-5, type=float, help="The initial learning rate for Adam.")
|
||||
parser.add_argument("--weight_decay", default=0.0, type=float, help="Weight decay if we apply some.")
|
||||
parser.add_argument("--adam_epsilon", default=1e-8, type=float, help="Epsilon for Adam optimizer.")
|
||||
parser.add_argument("--warmup_steps", default=0, type=int, help="Linear warmup over warmup_steps.")
|
||||
parser.add_argument("--warmup_steps", default=500, type=int, help="Linear warmup over warmup_steps.")
|
||||
parser.add_argument(
|
||||
"--num_train_epochs", default=3, type=int, help="Total number of training epochs to perform."
|
||||
)
|
||||
@@ -199,7 +241,8 @@ class LoggingCallback(pl.Callback):
|
||||
writer.write("{} = {}\n".format(key, str(metrics[key])))
|
||||
|
||||
|
||||
def add_generic_args(parser, root_dir):
|
||||
def add_generic_args(parser, root_dir) -> None:
|
||||
# TODO(SS): allow all pl args? parser = pl.Trainer.add_argparse_args(parser)
|
||||
parser.add_argument(
|
||||
"--output_dir",
|
||||
default=None,
|
||||
@@ -221,8 +264,8 @@ def add_generic_args(parser, root_dir):
|
||||
help="For fp16: Apex AMP optimization level selected in ['O0', 'O1', 'O2', and 'O3']."
|
||||
"See details at https://nvidia.github.io/apex/amp.html",
|
||||
)
|
||||
|
||||
parser.add_argument("--n_gpu", type=int, default=1)
|
||||
parser.add_argument("--fast_dev_run", action="store_true")
|
||||
parser.add_argument("--gpus", type=int, default=1)
|
||||
parser.add_argument("--n_tpu_cores", type=int, default=0)
|
||||
parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.")
|
||||
parser.add_argument("--do_train", action="store_true", help="Whether to run training.")
|
||||
@@ -235,28 +278,32 @@ def add_generic_args(parser, root_dir):
|
||||
)
|
||||
|
||||
parser.add_argument("--seed", type=int, default=42, help="random seed for initialization")
|
||||
parser.add_argument("--resume_from_checkpoint", type=str, default=None)
|
||||
parser.add_argument("--val_check_interval", default=1.0, type=float)
|
||||
|
||||
|
||||
def generic_train(model: BaseTransformer, args: argparse.Namespace):
|
||||
def generic_train(
|
||||
model: BaseTransformer,
|
||||
args: argparse.Namespace,
|
||||
early_stopping_callback=False,
|
||||
logger=True, # can pass WandbLogger() here
|
||||
extra_callbacks=[],
|
||||
checkpoint_callback=None,
|
||||
logging_callback=None,
|
||||
**extra_train_kwargs
|
||||
):
|
||||
# init model
|
||||
set_seed(args)
|
||||
odir = Path(model.hparams.output_dir)
|
||||
odir.mkdir(exist_ok=True)
|
||||
if checkpoint_callback is None:
|
||||
checkpoint_callback = pl.callbacks.ModelCheckpoint(
|
||||
filepath=args.output_dir, prefix="checkpoint", monitor="val_loss", mode="min", save_top_k=1
|
||||
)
|
||||
if logging_callback is None:
|
||||
logging_callback = LoggingCallback()
|
||||
|
||||
if os.path.exists(args.output_dir) and os.listdir(args.output_dir) and args.do_train:
|
||||
raise ValueError("Output directory ({}) already exists and is not empty.".format(args.output_dir))
|
||||
|
||||
checkpoint_callback = pl.callbacks.ModelCheckpoint(
|
||||
filepath=args.output_dir, prefix="checkpoint", monitor="val_loss", mode="min", save_top_k=5
|
||||
)
|
||||
|
||||
train_params = dict(
|
||||
accumulate_grad_batches=args.gradient_accumulation_steps,
|
||||
gpus=args.n_gpu,
|
||||
max_epochs=args.num_train_epochs,
|
||||
early_stop_callback=False,
|
||||
gradient_clip_val=args.max_grad_norm,
|
||||
checkpoint_callback=checkpoint_callback,
|
||||
callbacks=[LoggingCallback()],
|
||||
)
|
||||
train_params = {}
|
||||
|
||||
if args.fp16:
|
||||
train_params["use_amp"] = args.fp16
|
||||
@@ -269,12 +316,27 @@ def generic_train(model: BaseTransformer, args: argparse.Namespace):
|
||||
train_params["num_tpu_cores"] = args.n_tpu_cores
|
||||
train_params["gpus"] = 0
|
||||
|
||||
if args.n_gpu > 1:
|
||||
if args.gpus > 1:
|
||||
train_params["distributed_backend"] = "ddp"
|
||||
|
||||
trainer = pl.Trainer(**train_params)
|
||||
trainer = pl.Trainer(
|
||||
logger=logger,
|
||||
accumulate_grad_batches=args.gradient_accumulation_steps,
|
||||
gpus=args.gpus,
|
||||
max_epochs=args.num_train_epochs,
|
||||
early_stop_callback=early_stopping_callback,
|
||||
gradient_clip_val=args.max_grad_norm,
|
||||
checkpoint_callback=checkpoint_callback,
|
||||
callbacks=[logging_callback] + extra_callbacks,
|
||||
fast_dev_run=args.fast_dev_run,
|
||||
val_check_interval=args.val_check_interval,
|
||||
weights_summary=None,
|
||||
resume_from_checkpoint=args.resume_from_checkpoint,
|
||||
**train_params,
|
||||
)
|
||||
|
||||
if args.do_train:
|
||||
trainer.fit(model)
|
||||
|
||||
trainer.logger.log_hyperparams(args)
|
||||
trainer.logger.save()
|
||||
return trainer
|
||||
@@ -0,0 +1,5 @@
|
||||
# Long Form Question Answering
|
||||
|
||||
This folder contains the code for the Long Form Question answering [demo](http://35.226.96.115:8080/) as well as methods to train and use a fully end-to-end Long Form Question Answering system using the [🤗transformers](https://github.com/huggingface/transformers) and [🤗nlp](https://github.com/huggingface/nlp) libraries.
|
||||
|
||||
You can use these methods to train your own system by following along the associate [notebook](https://github.com/huggingface/notebooks/blob/master/longform-qa/Long_Form_Question_Answering_with_ELI5_and_Wikipedia.ipynb) or [blog post](https://yjernite.github.io/lfqa.html).
|
||||
@@ -0,0 +1,332 @@
|
||||
import faiss
|
||||
import nlp
|
||||
import numpy as np
|
||||
import torch
|
||||
from elasticsearch import Elasticsearch
|
||||
|
||||
import streamlit as st
|
||||
import transformers
|
||||
from eli5_utils import (
|
||||
embed_questions_for_retrieval,
|
||||
make_qa_s2s_model,
|
||||
qa_s2s_generate,
|
||||
query_es_index,
|
||||
query_qa_dense_index,
|
||||
)
|
||||
from transformers import AutoModel, AutoModelForSeq2SeqLM, AutoTokenizer
|
||||
|
||||
|
||||
MODEL_TYPE = "bart"
|
||||
LOAD_DENSE_INDEX = True
|
||||
|
||||
|
||||
@st.cache(allow_output_mutation=True)
|
||||
def load_models():
|
||||
if LOAD_DENSE_INDEX:
|
||||
qar_tokenizer = AutoTokenizer.from_pretrained("yjernite/retribert-base-uncased")
|
||||
qar_model = AutoModel.from_pretrained("yjernite/retribert-base-uncased").to("cuda:0")
|
||||
_ = qar_model.eval()
|
||||
else:
|
||||
qar_tokenizer, qar_model = (None, None)
|
||||
if MODEL_TYPE == "bart":
|
||||
s2s_tokenizer = AutoTokenizer.from_pretrained("yjernite/bart_eli5")
|
||||
s2s_model = AutoModelForSeq2SeqLM.from_pretrained("yjernite/bart_eli5").to("cuda:0")
|
||||
save_dict = torch.load("seq2seq_models/eli5_bart_model_blm_2.pth")
|
||||
s2s_model.load_state_dict(save_dict["model"])
|
||||
_ = s2s_model.eval()
|
||||
else:
|
||||
s2s_tokenizer, s2s_model = make_qa_s2s_model(
|
||||
model_name="t5-small", from_file="seq2seq_models/eli5_t5_model_1024_4.pth", device="cuda:0"
|
||||
)
|
||||
return (qar_tokenizer, qar_model, s2s_tokenizer, s2s_model)
|
||||
|
||||
|
||||
@st.cache(allow_output_mutation=True)
|
||||
def load_indexes():
|
||||
if LOAD_DENSE_INDEX:
|
||||
faiss_res = faiss.StandardGpuResources()
|
||||
wiki40b_passages = nlp.load_dataset(path="wiki_snippets", name="wiki40b_en_100_0")["train"]
|
||||
wiki40b_passage_reps = np.memmap(
|
||||
"wiki40b_passages_reps_32_l-8_h-768_b-512-512.dat",
|
||||
dtype="float32",
|
||||
mode="r",
|
||||
shape=(wiki40b_passages.num_rows, 128),
|
||||
)
|
||||
wiki40b_index_flat = faiss.IndexFlatIP(128)
|
||||
wiki40b_gpu_index_flat = faiss.index_cpu_to_gpu(faiss_res, 1, wiki40b_index_flat)
|
||||
wiki40b_gpu_index_flat.add(wiki40b_passage_reps) # TODO fix for larger GPU
|
||||
else:
|
||||
wiki40b_passages, wiki40b_gpu_index_flat = (None, None)
|
||||
es_client = Elasticsearch([{"host": "localhost", "port": "9200"}])
|
||||
return (wiki40b_passages, wiki40b_gpu_index_flat, es_client)
|
||||
|
||||
|
||||
@st.cache(allow_output_mutation=True)
|
||||
def load_train_data():
|
||||
eli5 = nlp.load_dataset("eli5", name="LFQA_reddit")
|
||||
eli5_train = eli5["train_eli5"]
|
||||
eli5_train_q_reps = np.memmap(
|
||||
"eli5_questions_reps.dat", dtype="float32", mode="r", shape=(eli5_train.num_rows, 128)
|
||||
)
|
||||
eli5_train_q_index = faiss.IndexFlatIP(128)
|
||||
eli5_train_q_index.add(eli5_train_q_reps)
|
||||
return (eli5_train, eli5_train_q_index)
|
||||
|
||||
|
||||
passages, gpu_dense_index, es_client = load_indexes()
|
||||
qar_tokenizer, qar_model, s2s_tokenizer, s2s_model = load_models()
|
||||
eli5_train, eli5_train_q_index = load_train_data()
|
||||
|
||||
|
||||
def find_nearest_training(question, n_results=10):
|
||||
q_rep = embed_questions_for_retrieval([question], qar_tokenizer, qar_model)
|
||||
D, I = eli5_train_q_index.search(q_rep, n_results)
|
||||
nn_examples = [eli5_train[int(i)] for i in I[0]]
|
||||
return nn_examples
|
||||
|
||||
|
||||
def make_support(question, source="wiki40b", method="dense", n_results=10):
|
||||
if source == "none":
|
||||
support_doc, hit_lst = (" <P> ".join(["" for _ in range(11)]).strip(), [])
|
||||
else:
|
||||
if method == "dense":
|
||||
support_doc, hit_lst = query_qa_dense_index(
|
||||
question, qar_model, qar_tokenizer, passages, gpu_dense_index, n_results
|
||||
)
|
||||
else:
|
||||
support_doc, hit_lst = query_es_index(
|
||||
question, es_client, index_name="english_wiki40b_snippets_100w", n_results=n_results,
|
||||
)
|
||||
support_list = [
|
||||
(res["article_title"], res["section_title"].strip(), res["score"], res["passage_text"]) for res in hit_lst
|
||||
]
|
||||
question_doc = "question: {} context: {}".format(question, support_doc)
|
||||
return question_doc, support_list
|
||||
|
||||
|
||||
@st.cache(hash_funcs={torch.Tensor: (lambda _: None), transformers.tokenization_bart.BartTokenizer: (lambda _: None)})
|
||||
def answer_question(
|
||||
question_doc, s2s_model, s2s_tokenizer, min_len=64, max_len=256, sampling=False, n_beams=2, top_p=0.95, temp=0.8
|
||||
):
|
||||
with torch.no_grad():
|
||||
answer = qa_s2s_generate(
|
||||
question_doc,
|
||||
s2s_model,
|
||||
s2s_tokenizer,
|
||||
num_answers=1,
|
||||
num_beams=n_beams,
|
||||
min_len=min_len,
|
||||
max_len=max_len,
|
||||
do_sample=sampling,
|
||||
temp=temp,
|
||||
top_p=top_p,
|
||||
top_k=None,
|
||||
max_input_length=1024,
|
||||
device="cuda:0",
|
||||
)[0]
|
||||
return (answer, support_list)
|
||||
|
||||
|
||||
st.title("Long Form Question Answering with ELI5")
|
||||
|
||||
# Start sidebar
|
||||
header_html = "<img src='https://huggingface.co/front/assets/huggingface_logo.svg'>"
|
||||
header_full = """
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.img-container {
|
||||
padding-left: 90px;
|
||||
padding-right: 90px;
|
||||
padding-top: 50px;
|
||||
padding-bottom: 50px;
|
||||
background-color: #f0f3f9;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<span class="img-container"> <!-- Inline parent element -->
|
||||
%s
|
||||
</span>
|
||||
</body>
|
||||
</html>
|
||||
""" % (
|
||||
header_html,
|
||||
)
|
||||
st.sidebar.markdown(
|
||||
header_full, unsafe_allow_html=True,
|
||||
)
|
||||
|
||||
# Long Form QA with ELI5 and Wikipedia
|
||||
description = """
|
||||
This demo presents a model trained to [provide long-form answers to open-domain questions](https://yjernite.github.io/lfqa.html).
|
||||
First, a document retriever fetches a set of relevant Wikipedia passages given the question from the [Wiki40b](https://research.google/pubs/pub49029/) dataset,
|
||||
a pre-processed fixed snapshot of Wikipedia.
|
||||
"""
|
||||
st.sidebar.markdown(description, unsafe_allow_html=True)
|
||||
|
||||
action_list = [
|
||||
"Answer the question",
|
||||
"View the retrieved document only",
|
||||
"View the most similar ELI5 question and answer",
|
||||
"Show me everything, please!",
|
||||
]
|
||||
demo_options = st.sidebar.checkbox("Demo options")
|
||||
if demo_options:
|
||||
action_st = st.sidebar.selectbox("", action_list, index=3,)
|
||||
action = action_list.index(action_st)
|
||||
show_type = st.sidebar.selectbox("", ["Show full text of passages", "Show passage section titles"], index=0,)
|
||||
show_passages = show_type == "Show full text of passages"
|
||||
else:
|
||||
action = 3
|
||||
show_passages = True
|
||||
|
||||
retrieval_options = st.sidebar.checkbox("Retrieval options")
|
||||
if retrieval_options:
|
||||
retriever_info = """
|
||||
### Information retriever options
|
||||
|
||||
The **sparse** retriever uses ElasticSearch, while the **dense** retriever uses max-inner-product search between a question and passage embedding
|
||||
trained using the [ELI5](https://arxiv.org/abs/1907.09190) questions-answer pairs.
|
||||
The answer is then generated by sequence to sequence model which takes the question and retrieved document as input.
|
||||
"""
|
||||
st.sidebar.markdown(retriever_info)
|
||||
wiki_source = st.sidebar.selectbox("Which Wikipedia format should the model use?", ["wiki40b", "none"])
|
||||
index_type = st.sidebar.selectbox("Which Wikipedia indexer should the model use?", ["dense", "sparse", "mixed"])
|
||||
else:
|
||||
wiki_source = "wiki40b"
|
||||
index_type = "dense"
|
||||
|
||||
sampled = "beam"
|
||||
n_beams = 2
|
||||
min_len = 64
|
||||
max_len = 256
|
||||
top_p = None
|
||||
temp = None
|
||||
generate_options = st.sidebar.checkbox("Generation options")
|
||||
if generate_options:
|
||||
generate_info = """
|
||||
### Answer generation options
|
||||
|
||||
The sequence-to-sequence model was initialized with [BART](https://huggingface.co/facebook/bart-large)
|
||||
weights and fine-tuned on the ELI5 QA pairs and retrieved documents. You can use the model for greedy decoding with
|
||||
**beam** search, or **sample** from the decoder's output probabilities.
|
||||
"""
|
||||
st.sidebar.markdown(generate_info)
|
||||
sampled = st.sidebar.selectbox("Would you like to use beam search or sample an answer?", ["beam", "sampled"])
|
||||
min_len = st.sidebar.slider(
|
||||
"Minimum generation length", min_value=8, max_value=256, value=64, step=8, format=None, key=None
|
||||
)
|
||||
max_len = st.sidebar.slider(
|
||||
"Maximum generation length", min_value=64, max_value=512, value=256, step=16, format=None, key=None
|
||||
)
|
||||
if sampled == "beam":
|
||||
n_beams = st.sidebar.slider("Beam size", min_value=1, max_value=8, value=2, step=None, format=None, key=None)
|
||||
else:
|
||||
top_p = st.sidebar.slider(
|
||||
"Nucleus sampling p", min_value=0.1, max_value=1.0, value=0.95, step=0.01, format=None, key=None
|
||||
)
|
||||
temp = st.sidebar.slider(
|
||||
"Temperature", min_value=0.1, max_value=1.0, value=0.7, step=0.01, format=None, key=None
|
||||
)
|
||||
n_beams = None
|
||||
|
||||
# start main text
|
||||
questions_list = [
|
||||
"<MY QUESTION>",
|
||||
"How do people make chocolate?",
|
||||
"Why do we get a fever when we are sick?",
|
||||
"How can different animals perceive different colors?",
|
||||
"What is natural language processing?",
|
||||
"What's the best way to treat a sunburn?",
|
||||
"What exactly are vitamins ?",
|
||||
"How does nuclear energy provide electricity?",
|
||||
"What's the difference between viruses and bacteria?",
|
||||
"Why are flutes classified as woodwinds when most of them are made out of metal ?",
|
||||
"Why do people like drinking coffee even though it tastes so bad?",
|
||||
"What happens when wine ages? How does it make the wine taste better?",
|
||||
"If an animal is an herbivore, where does it get the protein that it needs to survive if it only eats grass?",
|
||||
"How can we set a date to the beginning or end of an artistic period? Doesn't the change happen gradually?",
|
||||
"How does New Zealand have so many large bird predators?",
|
||||
]
|
||||
question_s = st.selectbox(
|
||||
"What would you like to ask? ---- select <MY QUESTION> to enter a new query", questions_list, index=1,
|
||||
)
|
||||
if question_s == "<MY QUESTION>":
|
||||
question = st.text_input("Enter your question here:", "")
|
||||
else:
|
||||
question = question_s
|
||||
|
||||
if st.button("Show me!"):
|
||||
if action in [0, 1, 3]:
|
||||
if index_type == "mixed":
|
||||
_, support_list_dense = make_support(question, source=wiki_source, method="dense", n_results=10)
|
||||
_, support_list_sparse = make_support(question, source=wiki_source, method="sparse", n_results=10)
|
||||
support_list = []
|
||||
for res_d, res_s in zip(support_list_dense, support_list_sparse):
|
||||
if tuple(res_d) not in support_list:
|
||||
support_list += [tuple(res_d)]
|
||||
if tuple(res_s) not in support_list:
|
||||
support_list += [tuple(res_s)]
|
||||
support_list = support_list[:10]
|
||||
question_doc = "<P> " + " <P> ".join([res[-1] for res in support_list])
|
||||
else:
|
||||
question_doc, support_list = make_support(question, source=wiki_source, method=index_type, n_results=10)
|
||||
if action in [0, 3]:
|
||||
answer, support_list = answer_question(
|
||||
question_doc,
|
||||
s2s_model,
|
||||
s2s_tokenizer,
|
||||
min_len=min_len,
|
||||
max_len=int(max_len),
|
||||
sampling=(sampled == "sampled"),
|
||||
n_beams=n_beams,
|
||||
top_p=top_p,
|
||||
temp=temp,
|
||||
)
|
||||
st.markdown("### The model generated answer is:")
|
||||
st.write(answer)
|
||||
if action in [0, 1, 3] and wiki_source != "none":
|
||||
st.markdown("--- \n ### The model is drawing information from the following Wikipedia passages:")
|
||||
for i, res in enumerate(support_list):
|
||||
wiki_url = "https://en.wikipedia.org/wiki/{}".format(res[0].replace(" ", "_"))
|
||||
sec_titles = res[1].strip()
|
||||
if sec_titles == "":
|
||||
sections = "[{}]({})".format(res[0], wiki_url)
|
||||
else:
|
||||
sec_list = sec_titles.split(" & ")
|
||||
sections = " & ".join(
|
||||
["[{}]({}#{})".format(sec.strip(), wiki_url, sec.strip().replace(" ", "_")) for sec in sec_list]
|
||||
)
|
||||
st.markdown(
|
||||
"{0:02d} - **Article**: {1:<18} <br> _Section_: {2}".format(i + 1, res[0], sections),
|
||||
unsafe_allow_html=True,
|
||||
)
|
||||
if show_passages:
|
||||
st.write(
|
||||
'> <span style="font-family:arial; font-size:10pt;">' + res[-1] + "</span>", unsafe_allow_html=True
|
||||
)
|
||||
if action in [2, 3]:
|
||||
nn_train_list = find_nearest_training(question)
|
||||
train_exple = nn_train_list[0]
|
||||
st.markdown(
|
||||
"--- \n ### The most similar question in the ELI5 training set was: \n\n {}".format(train_exple["title"])
|
||||
)
|
||||
answers_st = [
|
||||
"{}. {}".format(i + 1, " \n".join([line.strip() for line in ans.split("\n") if line.strip() != ""]))
|
||||
for i, (ans, sc) in enumerate(zip(train_exple["answers"]["text"], train_exple["answers"]["score"]))
|
||||
if i == 0 or sc > 2
|
||||
]
|
||||
st.markdown("##### Its answers were: \n\n {}".format("\n".join(answers_st)))
|
||||
|
||||
|
||||
disclaimer = """
|
||||
---
|
||||
|
||||
**Disclaimer**
|
||||
|
||||
*The intent of this app is to provide some (hopefully entertaining) insights into the behavior of a current LFQA system.
|
||||
Evaluating biases of such a model and ensuring factual generations are still very much open research problems.
|
||||
Therefore, until some significant progress is achieved, we caution against using the generated answers for practical purposes.*
|
||||
"""
|
||||
st.sidebar.markdown(disclaimer, unsafe_allow_html=True)
|
||||
@@ -0,0 +1,653 @@
|
||||
import functools
|
||||
import math
|
||||
import os # noqa: F401
|
||||
from random import choice, randint
|
||||
from time import time
|
||||
|
||||
import faiss # noqa: F401
|
||||
import nlp # noqa: F401
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import torch
|
||||
import torch.utils.checkpoint as checkpoint
|
||||
from elasticsearch import Elasticsearch # noqa: F401
|
||||
from elasticsearch.helpers import bulk, streaming_bulk # noqa: F401
|
||||
from torch.utils.data import DataLoader, Dataset, RandomSampler, SequentialSampler
|
||||
from tqdm import tqdm
|
||||
|
||||
from transformers import AdamW, AutoModel, AutoModelForSeq2SeqLM, AutoTokenizer, get_linear_schedule_with_warmup
|
||||
|
||||
|
||||
pd.set_option("display.max_colwidth", None)
|
||||
|
||||
|
||||
###############
|
||||
# Sparse index
|
||||
###############
|
||||
def make_es_index_snippets(es_client, passages_dset, index_name="english_wiki_kilt_snippets_100w"):
|
||||
index_config = {
|
||||
"settings": {
|
||||
"number_of_shards": 1,
|
||||
"analysis": {"analyzer": {"stop_standard": {"type": "standard", " stopwords": "_english_"}}},
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"article_title": {"type": "text", "analyzer": "standard", "similarity": "BM25"},
|
||||
"section_title": {"type": "text", "analyzer": "standard", "similarity": "BM25"},
|
||||
"passage_text": {"type": "text", "analyzer": "standard", "similarity": "BM25"},
|
||||
}
|
||||
},
|
||||
}
|
||||
es_client.indices.create(index=index_name, body=index_config)
|
||||
number_of_docs = passages_dset.num_rows
|
||||
progress = tqdm(unit="docs", total=number_of_docs)
|
||||
successes = 0
|
||||
|
||||
def passage_generator():
|
||||
for passage in passages_dset:
|
||||
yield passage
|
||||
|
||||
# create the ES index
|
||||
for ok, action in streaming_bulk(client=es_client, index=index_name, actions=passage_generator(),):
|
||||
progress.update(1)
|
||||
successes += ok
|
||||
print("Indexed %d documents" % (successes,))
|
||||
|
||||
|
||||
def query_es_index(question, es_client, index_name="english_wiki_kilt_snippets_100w", n_results=10, min_length=20):
|
||||
q = question.lower()
|
||||
banned = ["how", "why", "what", "where", "which", "do", "does", "is", "?", "eli5", "eli5:"]
|
||||
q = " ".join([w for w in q.split() if w not in banned])
|
||||
response = es_client.search(
|
||||
index=index_name,
|
||||
body={
|
||||
"query": {
|
||||
"multi_match": {
|
||||
"query": q,
|
||||
"fields": ["article_title", "section_title", "passage_text^2"],
|
||||
"type": "cross_fields",
|
||||
}
|
||||
},
|
||||
"size": 2 * n_results,
|
||||
},
|
||||
)
|
||||
hits = response["hits"]["hits"]
|
||||
support_doc = "<P> " + " <P> ".join([hit["_source"]["passage_text"] for hit in hits])
|
||||
res_list = [dict([(k, hit["_source"][k]) for k in hit["_source"] if k != "passage_text"]) for hit in hits]
|
||||
for r, hit in zip(res_list, hits):
|
||||
r["passage_id"] = hit["_id"]
|
||||
r["score"] = hit["_score"]
|
||||
r["passage_text"] = hit["_source"]["passage_text"]
|
||||
res_list = [res for res in res_list if len(res["passage_text"].split()) > min_length][:n_results]
|
||||
return support_doc, res_list
|
||||
|
||||
|
||||
###############
|
||||
# ELI5 retriever training
|
||||
###############
|
||||
class ELI5DatasetQARetriver(Dataset):
|
||||
def __init__(self, examples_array, extra_answer_threshold=3, min_answer_length=64, training=True, n_samples=None):
|
||||
self.data = examples_array
|
||||
self.answer_thres = extra_answer_threshold
|
||||
self.min_length = min_answer_length
|
||||
self.training = training
|
||||
self.n_samples = self.data.num_rows if n_samples is None else n_samples
|
||||
|
||||
def __len__(self):
|
||||
return self.n_samples
|
||||
|
||||
def make_example(self, idx):
|
||||
example = self.data[idx]
|
||||
question = example["title"]
|
||||
if self.training:
|
||||
answers = [a for i, (a, sc) in enumerate(zip(example["answers"]["text"], example["answers"]["score"]))]
|
||||
answer_tab = choice(answers).split(" ")
|
||||
start_idx = randint(0, max(0, len(answer_tab) - self.min_length))
|
||||
answer_span = " ".join(answer_tab[start_idx:])
|
||||
else:
|
||||
answer_span = example["answers"]["text"][0]
|
||||
return (question, answer_span)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
return self.make_example(idx % self.data.num_rows)
|
||||
|
||||
|
||||
class RetrievalQAEmbedder(torch.nn.Module):
|
||||
def __init__(self, sent_encoder, dim):
|
||||
super(RetrievalQAEmbedder, self).__init__()
|
||||
self.sent_encoder = sent_encoder
|
||||
self.output_dim = 128
|
||||
self.project_q = torch.nn.Linear(dim, self.output_dim, bias=False)
|
||||
self.project_a = torch.nn.Linear(dim, self.output_dim, bias=False)
|
||||
self.ce_loss = torch.nn.CrossEntropyLoss(reduction="mean")
|
||||
|
||||
def embed_sentences_checkpointed(self, input_ids, attention_mask, checkpoint_batch_size=-1):
|
||||
# reproduces BERT forward pass with checkpointing
|
||||
if checkpoint_batch_size < 0 or input_ids.shape[0] < checkpoint_batch_size:
|
||||
return self.sent_encoder(input_ids, attention_mask=attention_mask)[1]
|
||||
else:
|
||||
# prepare implicit variables
|
||||
device = input_ids.device
|
||||
input_shape = input_ids.size()
|
||||
token_type_ids = torch.zeros(input_shape, dtype=torch.long, device=device)
|
||||
head_mask = [None] * self.sent_encoder.config.num_hidden_layers
|
||||
extended_attention_mask: torch.Tensor = self.sent_encoder.get_extended_attention_mask(
|
||||
attention_mask, input_shape, device
|
||||
)
|
||||
|
||||
# define function for checkpointing
|
||||
def partial_encode(*inputs):
|
||||
encoder_outputs = self.sent_encoder.encoder(inputs[0], attention_mask=inputs[1], head_mask=head_mask,)
|
||||
sequence_output = encoder_outputs[0]
|
||||
pooled_output = self.sent_encoder.pooler(sequence_output)
|
||||
return pooled_output
|
||||
|
||||
# run embedding layer on everything at once
|
||||
embedding_output = self.sent_encoder.embeddings(
|
||||
input_ids=input_ids, position_ids=None, token_type_ids=token_type_ids, inputs_embeds=None
|
||||
)
|
||||
# run encoding and pooling on one mini-batch at a time
|
||||
pooled_output_list = []
|
||||
for b in range(math.ceil(input_ids.shape[0] / checkpoint_batch_size)):
|
||||
b_embedding_output = embedding_output[b * checkpoint_batch_size : (b + 1) * checkpoint_batch_size]
|
||||
b_attention_mask = extended_attention_mask[b * checkpoint_batch_size : (b + 1) * checkpoint_batch_size]
|
||||
pooled_output = checkpoint.checkpoint(partial_encode, b_embedding_output, b_attention_mask)
|
||||
pooled_output_list.append(pooled_output)
|
||||
return torch.cat(pooled_output_list, dim=0)
|
||||
|
||||
def embed_questions(self, q_ids, q_mask, checkpoint_batch_size=-1):
|
||||
q_reps = self.embed_sentences_checkpointed(q_ids, q_mask, checkpoint_batch_size)
|
||||
return self.project_q(q_reps)
|
||||
|
||||
def embed_answers(self, a_ids, a_mask, checkpoint_batch_size=-1):
|
||||
a_reps = self.embed_sentences_checkpointed(a_ids, a_mask, checkpoint_batch_size)
|
||||
return self.project_a(a_reps)
|
||||
|
||||
def forward(self, q_ids, q_mask, a_ids, a_mask, checkpoint_batch_size=-1):
|
||||
device = q_ids.device
|
||||
q_reps = self.embed_questions(q_ids, q_mask, checkpoint_batch_size)
|
||||
a_reps = self.embed_answers(a_ids, a_mask, checkpoint_batch_size)
|
||||
compare_scores = torch.mm(q_reps, a_reps.t())
|
||||
loss_qa = self.ce_loss(compare_scores, torch.arange(compare_scores.shape[1]).to(device))
|
||||
loss_aq = self.ce_loss(compare_scores.t(), torch.arange(compare_scores.shape[0]).to(device))
|
||||
loss = (loss_qa + loss_aq) / 2
|
||||
return loss
|
||||
|
||||
|
||||
def make_qa_retriever_model(model_name="google/bert_uncased_L-8_H-512_A-8", from_file=None, device="cuda:0"):
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
||||
bert_model = AutoModel.from_pretrained(model_name).to(device)
|
||||
# run bert_model on a dummy batch to get output dimension
|
||||
d_ids = torch.LongTensor(
|
||||
[[bert_model.config.bos_token_id if bert_model.config.bos_token_id is not None else 1]]
|
||||
).to(device)
|
||||
d_mask = torch.LongTensor([[1]]).to(device)
|
||||
sent_dim = bert_model(d_ids, attention_mask=d_mask)[1].shape[-1]
|
||||
qa_embedder = RetrievalQAEmbedder(bert_model, sent_dim).to(device)
|
||||
if from_file is not None:
|
||||
param_dict = torch.load(from_file) # has model weights, optimizer, and scheduler states
|
||||
qa_embedder.load_state_dict(param_dict["model"])
|
||||
return tokenizer, qa_embedder
|
||||
|
||||
|
||||
def make_qa_retriever_batch(qa_list, tokenizer, max_len=64, device="cuda:0"):
|
||||
q_ls = [q for q, a in qa_list]
|
||||
a_ls = [a for q, a in qa_list]
|
||||
q_toks = tokenizer.batch_encode_plus(q_ls, max_length=max_len, pad_to_max_length=True)
|
||||
q_ids, q_mask = (
|
||||
torch.LongTensor(q_toks["input_ids"]).to(device),
|
||||
torch.LongTensor(q_toks["attention_mask"]).to(device),
|
||||
)
|
||||
a_toks = tokenizer.batch_encode_plus(a_ls, max_length=max_len, pad_to_max_length=True)
|
||||
a_ids, a_mask = (
|
||||
torch.LongTensor(a_toks["input_ids"]).to(device),
|
||||
torch.LongTensor(a_toks["attention_mask"]).to(device),
|
||||
)
|
||||
return (q_ids, q_mask, a_ids, a_mask)
|
||||
|
||||
|
||||
def train_qa_retriever_epoch(model, dataset, tokenizer, optimizer, scheduler, args, e=0):
|
||||
model.train()
|
||||
# make iterator
|
||||
train_sampler = RandomSampler(dataset)
|
||||
model_collate_fn = functools.partial(
|
||||
make_qa_retriever_batch, tokenizer=tokenizer, max_len=args.max_length, device="cuda:0"
|
||||
)
|
||||
data_loader = DataLoader(dataset, batch_size=args.batch_size, sampler=train_sampler, collate_fn=model_collate_fn)
|
||||
epoch_iterator = tqdm(data_loader, desc="Iteration", disable=True)
|
||||
# accumulate loss since last print
|
||||
loc_steps = 0
|
||||
loc_loss = 0.0
|
||||
st_time = time()
|
||||
for step, batch in enumerate(epoch_iterator):
|
||||
q_ids, q_mask, a_ids, a_mask = batch
|
||||
pre_loss = model(q_ids, q_mask, a_ids, a_mask, checkpoint_batch_size=args.checkpoint_batch_size)
|
||||
loss = pre_loss.sum()
|
||||
# optimizer
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
scheduler.step()
|
||||
model.zero_grad()
|
||||
# some printing within the epoch
|
||||
loc_loss += loss.item()
|
||||
loc_steps += 1
|
||||
if step % args.print_freq == 0 or step == 1:
|
||||
print(
|
||||
"{:2d} {:5d} of {:5d} \t L: {:.3f} \t -- {:.3f}".format(
|
||||
e, step, len(dataset) // args.batch_size, loc_loss / loc_steps, time() - st_time,
|
||||
)
|
||||
)
|
||||
loc_loss = 0
|
||||
loc_steps = 0
|
||||
|
||||
|
||||
def train_qa_retriever_joint_epoch(model, dataset_list, tokenizer, optimizer, scheduler, args, e=0):
|
||||
model.train()
|
||||
model_collate_fn = functools.partial(
|
||||
make_qa_retriever_batch, tokenizer=tokenizer, max_len=args.max_length, device="cuda:0"
|
||||
)
|
||||
# make iterator
|
||||
train_samplers = [RandomSampler(dataset) for dataset in dataset_list]
|
||||
data_loaders = [
|
||||
DataLoader(dataset, batch_size=args.batch_size, sampler=train_sampler, collate_fn=model_collate_fn)
|
||||
for dataset, train_sampler in zip(dataset_list, train_samplers)
|
||||
]
|
||||
iterators = [iter(dloader) for dloader in data_loaders]
|
||||
joint_iter = zip(*iterators)
|
||||
# accumulate loss since last print
|
||||
loc_steps = 0
|
||||
loc_loss = 0.0
|
||||
st_time = time()
|
||||
for step, (batches,) in enumerate(zip(joint_iter)):
|
||||
for batch in batches:
|
||||
q_ids, q_mask, a_ids, a_mask = batch
|
||||
loss = model(q_ids, q_mask, a_ids, a_mask, checkpoint_batch_size=args.checkpoint_batch_size)
|
||||
# optimizer
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
scheduler.step()
|
||||
model.zero_grad()
|
||||
# some printing within the epoch
|
||||
loc_loss += loss.item()
|
||||
loc_steps += 1
|
||||
if step % args.print_freq == 0:
|
||||
print(
|
||||
"{:2d} {:5d} of {:5d} \t L: {:.3f} \t -- {:.3f}".format(
|
||||
e, step, len(dataset_list[0]) // args.batch_size, loc_loss / loc_steps, time() - st_time,
|
||||
)
|
||||
)
|
||||
loc_loss = 0
|
||||
loc_steps = 0
|
||||
|
||||
|
||||
def evaluate_qa_retriever(model, dataset, tokenizer, args):
|
||||
model.eval()
|
||||
# make iterator
|
||||
eval_sampler = SequentialSampler(dataset)
|
||||
model_collate_fn = functools.partial(
|
||||
make_qa_retriever_batch, tokenizer=tokenizer, max_len=args.max_length, device="cuda:0"
|
||||
)
|
||||
data_loader = DataLoader(dataset, batch_size=args.batch_size, sampler=eval_sampler, collate_fn=model_collate_fn)
|
||||
epoch_iterator = tqdm(data_loader, desc="Iteration", disable=True)
|
||||
tot_loss = 0.0
|
||||
with torch.no_grad():
|
||||
for step, batch in enumerate(epoch_iterator):
|
||||
q_ids, q_mask, a_ids, a_mask = batch
|
||||
loss = model(q_ids, q_mask, a_ids, a_mask)
|
||||
tot_loss += loss.item()
|
||||
return tot_loss / (step + 1)
|
||||
|
||||
|
||||
def train_qa_retriever(qar_model, qar_tokenizer, qar_train_dset, qar_valid_dset, qar_args):
|
||||
qar_optimizer = AdamW(qar_model.parameters(), lr=qar_args.learning_rate, eps=1e-8)
|
||||
qar_scheduler = get_linear_schedule_with_warmup(
|
||||
qar_optimizer,
|
||||
num_warmup_steps=100,
|
||||
num_training_steps=(qar_args.num_epochs + 1) * math.ceil(len(qar_train_dset) / qar_args.batch_size),
|
||||
)
|
||||
for e in range(qar_args.num_epochs):
|
||||
train_qa_retriever_epoch(qar_model, qar_train_dset, qar_tokenizer, qar_optimizer, qar_scheduler, qar_args, e)
|
||||
m_save_dict = {
|
||||
"model": qar_model.state_dict(),
|
||||
"optimizer": qar_optimizer.state_dict(),
|
||||
"scheduler": qar_scheduler.state_dict(),
|
||||
}
|
||||
print("Saving model {}".format(qar_args.model_save_name))
|
||||
torch.save(m_save_dict, "{}_{}.pth".format(qar_args.model_save_name, e))
|
||||
eval_loss = evaluate_qa_retriever(qar_model, qar_valid_dset, qar_tokenizer, qar_args)
|
||||
print("Evaluation loss epoch {:4d}: {:.3f}".format(e, eval_loss))
|
||||
|
||||
|
||||
###############
|
||||
# ELI5 seq2seq model training
|
||||
###############
|
||||
class ELI5DatasetS2S(Dataset):
|
||||
def __init__(
|
||||
self, examples_array, make_doc_fun=None, extra_answer_threshold=3, document_cache=None, training=True
|
||||
):
|
||||
self.training = training
|
||||
self.data = examples_array
|
||||
self.make_doc_function = make_doc_fun
|
||||
self.document_cache = {} if document_cache is None else document_cache
|
||||
assert not (make_doc_fun is None and document_cache is None)
|
||||
# make index of specific question-answer pairs from multi-answers
|
||||
if self.training:
|
||||
self.qa_id_list = [
|
||||
(i, j)
|
||||
for i, qa in enumerate(self.data)
|
||||
for j, (a, sc) in enumerate(zip(qa["answers"]["text"], qa["answers"]["score"]))
|
||||
if j == 0 or sc >= extra_answer_threshold
|
||||
]
|
||||
else:
|
||||
self.qa_id_list = [(i, 0) for i in range(self.data.num_rows)]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.qa_id_list)
|
||||
|
||||
def make_example(self, idx):
|
||||
i, j = self.qa_id_list[idx]
|
||||
example = self.data[i]
|
||||
question = example["title"] + " " + example["selftext"]
|
||||
answer = example["answers"]["text"][j]
|
||||
q_id = example["q_id"]
|
||||
if self.make_doc_function is not None:
|
||||
self.document_cache[q_id] = self.document_cache.get(q_id, self.make_doc_function(example["title"]))
|
||||
document = self.document_cache[q_id]
|
||||
in_st = "question: {} context: {}".format(
|
||||
question.lower().replace(" --t--", "").strip(), document.lower().strip(),
|
||||
)
|
||||
out_st = answer
|
||||
return (in_st, out_st)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
return self.make_example(idx)
|
||||
|
||||
|
||||
def make_qa_s2s_model(model_name="facebook/bart-large", from_file=None, device="cuda:0"):
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
||||
model = AutoModelForSeq2SeqLM.from_pretrained(model_name).to(device)
|
||||
if from_file is not None:
|
||||
param_dict = torch.load(from_file) # has model weights, optimizer, and scheduler states
|
||||
model.load_state_dict(param_dict["model"])
|
||||
return tokenizer, model
|
||||
|
||||
|
||||
def make_qa_s2s_batch(qa_list, tokenizer, max_len=64, max_a_len=360, device="cuda:0"):
|
||||
q_ls = [q for q, a in qa_list]
|
||||
a_ls = [a for q, a in qa_list]
|
||||
q_toks = tokenizer.batch_encode_plus(q_ls, max_length=max_len, pad_to_max_length=True)
|
||||
q_ids, q_mask = (
|
||||
torch.LongTensor(q_toks["input_ids"]).to(device),
|
||||
torch.LongTensor(q_toks["attention_mask"]).to(device),
|
||||
)
|
||||
a_toks = tokenizer.batch_encode_plus(a_ls, max_length=min(max_len, max_a_len), pad_to_max_length=True)
|
||||
a_ids, a_mask = (
|
||||
torch.LongTensor(a_toks["input_ids"]).to(device),
|
||||
torch.LongTensor(a_toks["attention_mask"]).to(device),
|
||||
)
|
||||
lm_labels = a_ids[:, 1:].contiguous().clone()
|
||||
lm_labels[a_mask[:, 1:].contiguous() == 0] = -100
|
||||
model_inputs = {
|
||||
"input_ids": q_ids,
|
||||
"attention_mask": q_mask,
|
||||
"decoder_input_ids": a_ids[:, :-1].contiguous(),
|
||||
"lm_labels": lm_labels,
|
||||
}
|
||||
return model_inputs
|
||||
|
||||
|
||||
def train_qa_s2s_epoch(model, dataset, tokenizer, optimizer, scheduler, args, e=0, curriculum=False):
|
||||
model.train()
|
||||
# make iterator
|
||||
if curriculum:
|
||||
train_sampler = SequentialSampler(dataset)
|
||||
else:
|
||||
train_sampler = RandomSampler(dataset)
|
||||
model_collate_fn = functools.partial(
|
||||
make_qa_s2s_batch, tokenizer=tokenizer, max_len=args.max_length, device="cuda:0"
|
||||
)
|
||||
data_loader = DataLoader(dataset, batch_size=args.batch_size, sampler=train_sampler, collate_fn=model_collate_fn)
|
||||
epoch_iterator = tqdm(data_loader, desc="Iteration", disable=True)
|
||||
# accumulate loss since last print
|
||||
loc_steps = 0
|
||||
loc_loss = 0.0
|
||||
st_time = time()
|
||||
for step, batch_inputs in enumerate(epoch_iterator):
|
||||
pre_loss = model(**batch_inputs)[0]
|
||||
loss = pre_loss.sum() / pre_loss.shape[0]
|
||||
loss.backward()
|
||||
# optimizer
|
||||
if step % args.backward_freq == 0:
|
||||
optimizer.step()
|
||||
scheduler.step()
|
||||
model.zero_grad()
|
||||
# some printing within the epoch
|
||||
loc_loss += loss.item()
|
||||
loc_steps += 1
|
||||
if step % args.print_freq == 0 or step == 1:
|
||||
print(
|
||||
"{:2d} {:5d} of {:5d} \t L: {:.3f} \t -- {:.3f}".format(
|
||||
e, step, len(dataset) // args.batch_size, loc_loss / loc_steps, time() - st_time,
|
||||
)
|
||||
)
|
||||
loc_loss = 0
|
||||
loc_steps = 0
|
||||
|
||||
|
||||
def eval_qa_s2s_epoch(model, dataset, tokenizer, args):
|
||||
model.eval()
|
||||
# make iterator
|
||||
train_sampler = SequentialSampler(dataset)
|
||||
model_collate_fn = functools.partial(
|
||||
make_qa_s2s_batch, tokenizer=tokenizer, max_len=args.max_length, device="cuda:0"
|
||||
)
|
||||
data_loader = DataLoader(dataset, batch_size=args.batch_size, sampler=train_sampler, collate_fn=model_collate_fn)
|
||||
epoch_iterator = tqdm(data_loader, desc="Iteration", disable=True)
|
||||
# accumulate loss since last print
|
||||
loc_steps = 0
|
||||
loc_loss = 0.0
|
||||
st_time = time()
|
||||
with torch.no_grad():
|
||||
for step, batch_inputs in enumerate(epoch_iterator):
|
||||
pre_loss = model(**batch_inputs)[0]
|
||||
loss = pre_loss.sum() / pre_loss.shape[0]
|
||||
loc_loss += loss.item()
|
||||
loc_steps += 1
|
||||
if step % args.print_freq == 0:
|
||||
print(
|
||||
"{:5d} of {:5d} \t L: {:.3f} \t -- {:.3f}".format(
|
||||
step, len(dataset) // args.batch_size, loc_loss / loc_steps, time() - st_time,
|
||||
)
|
||||
)
|
||||
print("Total \t L: {:.3f} \t -- {:.3f}".format(loc_loss / loc_steps, time() - st_time,))
|
||||
|
||||
|
||||
def train_qa_s2s(qa_s2s_model, qa_s2s_tokenizer, s2s_train_dset, s2s_valid_dset, s2s_args):
|
||||
s2s_optimizer = AdamW(qa_s2s_model.parameters(), lr=s2s_args.learning_rate, eps=1e-8)
|
||||
s2s_scheduler = get_linear_schedule_with_warmup(
|
||||
s2s_optimizer,
|
||||
num_warmup_steps=400,
|
||||
num_training_steps=(s2s_args.num_epochs + 1) * math.ceil(len(s2s_train_dset) / s2s_args.batch_size),
|
||||
)
|
||||
for e in range(s2s_args.num_epochs):
|
||||
train_qa_s2s_epoch(
|
||||
qa_s2s_model,
|
||||
s2s_train_dset,
|
||||
qa_s2s_tokenizer,
|
||||
s2s_optimizer,
|
||||
s2s_scheduler,
|
||||
s2s_args,
|
||||
e,
|
||||
curriculum=(e == 0),
|
||||
)
|
||||
m_save_dict = {
|
||||
"model": qa_s2s_model.state_dict(),
|
||||
"optimizer": s2s_optimizer.state_dict(),
|
||||
"scheduler": s2s_scheduler.state_dict(),
|
||||
}
|
||||
print("Saving model {}".format(s2s_args.model_save_name))
|
||||
eval_qa_s2s_epoch(qa_s2s_model, s2s_valid_dset, qa_s2s_tokenizer, s2s_args)
|
||||
torch.save(m_save_dict, "{}_{}.pth".format(s2s_args.model_save_name, e))
|
||||
|
||||
|
||||
# generate answer from input "question: ... context: <p> ..."
|
||||
def qa_s2s_generate(
|
||||
question_doc,
|
||||
qa_s2s_model,
|
||||
qa_s2s_tokenizer,
|
||||
num_answers=1,
|
||||
num_beams=None,
|
||||
min_len=64,
|
||||
max_len=256,
|
||||
do_sample=False,
|
||||
temp=1.0,
|
||||
top_p=None,
|
||||
top_k=None,
|
||||
max_input_length=512,
|
||||
device="cuda:0",
|
||||
):
|
||||
model_inputs = make_qa_s2s_batch([(question_doc, "A")], qa_s2s_tokenizer, max_input_length, device=device,)
|
||||
n_beams = num_answers if num_beams is None else max(num_beams, num_answers)
|
||||
generated_ids = qa_s2s_model.generate(
|
||||
input_ids=model_inputs["input_ids"],
|
||||
attention_mask=model_inputs["attention_mask"],
|
||||
min_length=min_len,
|
||||
max_length=max_len,
|
||||
do_sample=do_sample,
|
||||
early_stopping=True,
|
||||
num_beams=1 if do_sample else n_beams,
|
||||
temperature=temp,
|
||||
top_k=top_k,
|
||||
top_p=top_p,
|
||||
eos_token_id=qa_s2s_tokenizer.eos_token_id,
|
||||
no_repeat_ngram_size=3,
|
||||
num_return_sequences=num_answers,
|
||||
decoder_start_token_id=qa_s2s_tokenizer.bos_token_id,
|
||||
)
|
||||
return [qa_s2s_tokenizer.decode(ans_ids, skip_special_tokens=True).strip() for ans_ids in generated_ids]
|
||||
|
||||
|
||||
###############
|
||||
# ELI5-trained retrieval model usage
|
||||
###############
|
||||
def embed_passages_for_retrieval(passages, tokenizer, qa_embedder, max_length=128, device="cuda:0"):
|
||||
a_toks = tokenizer.batch_encode_plus(passages, max_length=max_length, pad_to_max_length=True)
|
||||
a_ids, a_mask = (
|
||||
torch.LongTensor(a_toks["input_ids"]).to(device),
|
||||
torch.LongTensor(a_toks["attention_mask"]).to(device),
|
||||
)
|
||||
with torch.no_grad():
|
||||
a_reps = qa_embedder.embed_answers(a_ids, a_mask).cpu().type(torch.float)
|
||||
return a_reps.numpy()
|
||||
|
||||
|
||||
def embed_questions_for_retrieval(q_ls, tokenizer, qa_embedder, device="cuda:0"):
|
||||
q_toks = tokenizer.batch_encode_plus(q_ls, max_length=128, pad_to_max_length=True)
|
||||
q_ids, q_mask = (
|
||||
torch.LongTensor(q_toks["input_ids"]).to(device),
|
||||
torch.LongTensor(q_toks["attention_mask"]).to(device),
|
||||
)
|
||||
with torch.no_grad():
|
||||
q_reps = qa_embedder.embed_questions(q_ids, q_mask).cpu().type(torch.float)
|
||||
return q_reps.numpy()
|
||||
|
||||
|
||||
def make_qa_dense_index(
|
||||
qa_embedder,
|
||||
tokenizer,
|
||||
passages_dset,
|
||||
batch_size=512,
|
||||
max_length=128,
|
||||
index_name="kilt_passages_reps.dat",
|
||||
dtype="float32",
|
||||
device="cuda:0",
|
||||
):
|
||||
st_time = time()
|
||||
fp = np.memmap(index_name, dtype=dtype, mode="w+", shape=(passages_dset.num_rows, 128))
|
||||
n_batches = math.ceil(passages_dset.num_rows / batch_size)
|
||||
for i in range(n_batches):
|
||||
passages = [p for p in passages_dset[i * batch_size : (i + 1) * batch_size]["passage_text"]]
|
||||
reps = embed_passages_for_retrieval(passages, tokenizer, qa_embedder, max_length, device)
|
||||
fp[i * batch_size : (i + 1) * batch_size] = reps
|
||||
if i % 50 == 0:
|
||||
print(i, time() - st_time)
|
||||
|
||||
|
||||
def evaluate_retriever(qa_list, retriever_func, scoring_func, n_ret=10, verbose=False):
|
||||
total_retriever_time = 0.0
|
||||
total_retriever_score = 0.0
|
||||
st_time = time()
|
||||
for i, (question, answer) in enumerate(qa_list):
|
||||
r_time = time()
|
||||
retrieved_passages = retriever_func(question, n_ret)
|
||||
total_retriever_time += time() - r_time
|
||||
total_retriever_score += scoring_func(retrieved_passages, answer)
|
||||
if verbose and ((i + 1) % 500 == 0 or i <= 1):
|
||||
print(
|
||||
"{:03d}: S-{:.4f} T-{:.4f} | {:.2f}".format(
|
||||
i + 1, total_retriever_score / (i + 1), total_retriever_time / (i + 1), time() - st_time
|
||||
)
|
||||
)
|
||||
return {"idf_recall": total_retriever_score / (i + 1), "retrieval_time": total_retriever_time / (i + 1)}
|
||||
|
||||
|
||||
# build a support document for the question out of Wikipedia snippets
|
||||
def query_qa_dense_index(
|
||||
question, qa_embedder, tokenizer, wiki_passages, wiki_index, n_results=10, min_length=20, device="cuda:0"
|
||||
):
|
||||
q_rep = embed_questions_for_retrieval([question], tokenizer, qa_embedder, device=device)
|
||||
D, I = wiki_index.search(q_rep, 2 * n_results)
|
||||
res_passages = [wiki_passages[int(i)] for i in I[0]]
|
||||
support_doc = "<P> " + " <P> ".join([p["passage_text"] for p in res_passages])
|
||||
res_list = [dict([(k, p[k]) for k in wiki_passages.column_names]) for p in res_passages]
|
||||
res_list = [res for res in res_list if len(res["passage_text"].split()) > min_length][:n_results]
|
||||
for r, sc in zip(res_list, D[0]):
|
||||
r["score"] = float(sc)
|
||||
return support_doc, res_list
|
||||
|
||||
|
||||
def batch_query_qa_dense_index(questions, qa_embedder, tokenizer, wiki_passages, wiki_index, n_results=10):
|
||||
q_rep = embed_questions_for_retrieval(questions, tokenizer, qa_embedder)
|
||||
D, I = wiki_index.search(q_rep, n_results)
|
||||
res_passages_lst = [[wiki_passages[int(i)] for i in i_lst] for i_lst in I]
|
||||
support_doc_lst = [
|
||||
"<P> " + " <P> ".join([p["passage_text"] for p in res_passages]) for res_passages in res_passages_lst
|
||||
]
|
||||
all_res_lists = []
|
||||
for (res_passages, dl) in zip(res_passages_lst, D):
|
||||
res_list = [dict([(k, p[k]) for k in wiki_passages.column_names]) for p in res_passages]
|
||||
for r, sc in zip(res_list, dl):
|
||||
r["score"] = float(sc)
|
||||
all_res_lists += [res_list[:]]
|
||||
return support_doc_lst, all_res_lists
|
||||
|
||||
|
||||
# find nearest neighbors of an answer or declarative text in Wikipedia snippets
|
||||
def query_qa_dense_index_nn(passage, qa_embedder, tokenizer, wiki_passages, wiki_index, n_results=10, min_length=20):
|
||||
a_rep = embed_passages_for_retrieval([passage], tokenizer, qa_embedder)
|
||||
D, I = wiki_index.search(a_rep, 2 * n_results)
|
||||
res_passages = [wiki_passages[int(i)] for i in I[0]]
|
||||
support_doc = "<P> " + " <P> ".join([p["passage_text"] for p in res_passages])
|
||||
res_list = [dict([(k, p[k]) for k in wiki_passages.column_names]) for p in res_passages]
|
||||
res_list = [res for res in res_list if len(res["passage_text"].split()) > min_length][:n_results]
|
||||
for r, sc, i in zip(res_list, D[0], I[0]):
|
||||
r["passage_id"] = int(i)
|
||||
r["score"] = float(sc)
|
||||
return support_doc, res_list
|
||||
|
||||
|
||||
def batch_query_qa_dense_index_nn(passages, qa_embedder, tokenizer, wiki_passages, wiki_index, n_results=10):
|
||||
a_reps = embed_passages_for_retrieval(passages, tokenizer, qa_embedder)
|
||||
D, I = wiki_index.search(a_reps, n_results)
|
||||
res_passages_lst = [[wiki_passages[int(i)] for i in i_lst] for i_lst in I]
|
||||
support_doc_lst = [
|
||||
"<P> " + " <P> ".join([p["passage_text"] for p in res_passages]) for res_passages in res_passages_lst
|
||||
]
|
||||
all_res_lists = []
|
||||
for (res_passages, dl, il) in zip(res_passages_lst, D, I):
|
||||
res_list = [dict([(k, p[k]) for k in wiki_passages.column_names]) for p in res_passages]
|
||||
for r, sc, i in zip(res_list, dl, il):
|
||||
r["passage_id"] = int(i)
|
||||
r["score"] = float(sc)
|
||||
all_res_lists += [res_list[:]]
|
||||
return support_doc_lst, all_res_lists
|
||||
@@ -140,7 +140,7 @@ Regularization based pruning methods (soft movement pruning and L0 regularizatio
|
||||
To obtain the effective sparsity level in the encoder, we simply count the number of activated (non-null) weights:
|
||||
|
||||
```bash
|
||||
python examples/movement-pruning/count_parameters.py \
|
||||
python examples/movement-pruning/counts_parameters.py \
|
||||
--pruning_method sigmoied_threshold \
|
||||
--threshold 0.1 \
|
||||
--serialization_dir $SERIALIZATION_DIR
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
"\n",
|
||||
"We experiment with a question answering model with only 6% of total remaining weights in the encoder (previously obtained with movement pruning). **We are able to reduce the memory size of the encoder from 340MB (original dense BERT) to 11MB**, which fits on a [91' floppy disk](https://en.wikipedia.org/wiki/Floptical)!\n",
|
||||
"\n",
|
||||
"<img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/0/00/Floptical_disk_21MB.jpg/440px-Floptical_disk_21MB.jpg\" width=\"200\">"
|
||||
"<img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/0/00/Floptical_disk_21MB.jpg/440px-Floptical_disk_21MB.jpg\" width=\"200\">\n",
|
||||
"\n",
|
||||
"*Note: this notebook is compatible with `torch>=1.5.0` If you are using, `torch==1.4.0`, please refer to [this previous version of the notebook](https://github.com/huggingface/transformers/commit/b11386e158e86e62d4041eabd86d044cd1695737).*"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -67,10 +69,7 @@
|
||||
"source": [
|
||||
"# Load fine-pruned model and quantize the model\n",
|
||||
"\n",
|
||||
"model_path = \"serialization_dir/bert-base-uncased/92/squad/l1\"\n",
|
||||
"model_name = \"bertarized_l1_with_distil_0._0.1_1_2_l1_1100._3e-5_1e-2_sigmoied_threshold_constant_0._10_epochs\"\n",
|
||||
"\n",
|
||||
"model = BertForQuestionAnswering.from_pretrained(os.path.join(model_path, model_name))\n",
|
||||
"model = BertForQuestionAnswering.from_pretrained(\"huggingface/prunebert-base-uncased-6-finepruned-w-distil-squad\")\n",
|
||||
"model.to('cpu')\n",
|
||||
"\n",
|
||||
"quantized_model = torch.quantization.quantize_dynamic(\n",
|
||||
@@ -196,7 +195,7 @@
|
||||
"\n",
|
||||
"elementary_qtz_st = {}\n",
|
||||
"for name, param in qtz_st.items():\n",
|
||||
" if param.is_quantized:\n",
|
||||
" if \"dtype\" not in name and param.is_quantized:\n",
|
||||
" print(\"Decompose quantization for\", name)\n",
|
||||
" # We need to extract the scale, the zero_point and the int_repr for the quantized tensor and modules\n",
|
||||
" scale = param.q_scale() # torch.tensor(1,) - float32\n",
|
||||
@@ -221,6 +220,17 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create mapping from torch.dtype to string description (we could also used an int8 instead of string)\n",
|
||||
"str_2_dtype = {\"qint8\": torch.qint8}\n",
|
||||
"dtype_2_str = {torch.qint8: \"qint8\"}\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
@@ -245,7 +255,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -266,9 +276,10 @@
|
||||
"Skip bert.pooler.dense._packed_params.weight.int_repr.indices\n",
|
||||
"Skip bert.pooler.dense._packed_params.weight.int_repr.shape\n",
|
||||
"Skip bert.pooler.dense._packed_params.bias\n",
|
||||
"Skip bert.pooler.dense._packed_params.dtype\n",
|
||||
"\n",
|
||||
"Encoder Size (MB) - Dense: 340.25\n",
|
||||
"Encoder Size (MB) - Sparse & Quantized: 11.27\n"
|
||||
"Encoder Size (MB) - Dense: 340.26\n",
|
||||
"Encoder Size (MB) - Sparse & Quantized: 11.28\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -300,10 +311,14 @@
|
||||
"\n",
|
||||
" elif type(param) == float or type(param) == int or type(param) == tuple:\n",
|
||||
" # float - tensor _packed_params.weight.scale\n",
|
||||
" # int - tensor_packed_params.weight.zero_point\n",
|
||||
" # int - tensor _packed_params.weight.zero_point\n",
|
||||
" # tuple - tensor _packed_params.weight.shape\n",
|
||||
" hf.attrs[name] = param\n",
|
||||
"\n",
|
||||
" elif type(param) == torch.dtype:\n",
|
||||
" # dtype - tensor _packed_params.dtype\n",
|
||||
" hf.attrs[name] = dtype_2_str[param]\n",
|
||||
" \n",
|
||||
" else:\n",
|
||||
" hf.create_dataset(name, data=param, compression=\"gzip\", compression_opts=9)\n",
|
||||
"\n",
|
||||
@@ -319,7 +334,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -327,7 +342,7 @@
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"Size (MB): 99.39\n"
|
||||
"Size (MB): 99.41\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -363,10 +378,15 @@
|
||||
" # tuple - tensor _packed_params.weight.shape\n",
|
||||
" hf.attrs[name] = param\n",
|
||||
"\n",
|
||||
" elif type(param) == torch.dtype:\n",
|
||||
" # dtype - tensor _packed_params.dtype\n",
|
||||
" hf.attrs[name] = dtype_2_str[param]\n",
|
||||
" \n",
|
||||
" else:\n",
|
||||
" hf.create_dataset(name, data=param, compression=\"gzip\", compression_opts=9)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"with open('dbg/metadata.json', 'w') as f:\n",
|
||||
" f.write(json.dumps(qtz_st._metadata)) \n",
|
||||
"\n",
|
||||
@@ -383,7 +403,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -406,6 +426,8 @@
|
||||
" attr_param = int(attr_param)\n",
|
||||
" else:\n",
|
||||
" attr_param = torch.tensor(attr_param)\n",
|
||||
" elif \".dtype\" in attr_name:\n",
|
||||
" attr_param = str_2_dtype[attr_param]\n",
|
||||
" reconstructed_elementary_qtz_st[attr_name] = attr_param\n",
|
||||
" # print(f\"Unpack {attr_name}\")\n",
|
||||
" \n",
|
||||
@@ -428,7 +450,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -451,7 +473,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -487,7 +509,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -517,7 +539,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -526,7 +548,7 @@
|
||||
"<All keys matched successfully>"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -553,7 +575,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
|
||||
@@ -121,16 +121,7 @@ if is_torch_available():
|
||||
else:
|
||||
examples = processor.get_train_examples(data_dir)
|
||||
logger.info("Training examples: %s", len(examples))
|
||||
# TODO clean up all this to leverage built-in features of tokenizers
|
||||
self.features = convert_examples_to_features(
|
||||
examples,
|
||||
label_list,
|
||||
max_seq_length,
|
||||
tokenizer,
|
||||
pad_on_left=bool(tokenizer.padding_side == "left"),
|
||||
pad_token=tokenizer.pad_token_id,
|
||||
pad_token_segment_id=tokenizer.pad_token_type_id,
|
||||
)
|
||||
self.features = convert_examples_to_features(examples, label_list, max_seq_length, tokenizer,)
|
||||
logger.info("Saving features into cached file %s", cached_features_file)
|
||||
torch.save(self.features, cached_features_file)
|
||||
|
||||
@@ -172,16 +163,8 @@ if is_tf_available():
|
||||
else:
|
||||
examples = processor.get_train_examples(data_dir)
|
||||
logger.info("Training examples: %s", len(examples))
|
||||
# TODO clean up all this to leverage built-in features of tokenizers
|
||||
self.features = convert_examples_to_features(
|
||||
examples,
|
||||
label_list,
|
||||
max_seq_length,
|
||||
tokenizer,
|
||||
pad_on_left=bool(tokenizer.padding_side == "left"),
|
||||
pad_token=tokenizer.pad_token_id,
|
||||
pad_token_segment_id=tokenizer.pad_token_type_id,
|
||||
)
|
||||
|
||||
self.features = convert_examples_to_features(examples, label_list, max_seq_length, tokenizer,)
|
||||
|
||||
def gen():
|
||||
for (ex_index, ex) in tqdm.tqdm(enumerate(self.features), desc="convert examples to features"):
|
||||
@@ -506,14 +489,7 @@ class ArcProcessor(DataProcessor):
|
||||
|
||||
|
||||
def convert_examples_to_features(
|
||||
examples: List[InputExample],
|
||||
label_list: List[str],
|
||||
max_length: int,
|
||||
tokenizer: PreTrainedTokenizer,
|
||||
pad_token_segment_id=0,
|
||||
pad_on_left=False,
|
||||
pad_token=0,
|
||||
mask_padding_with_zero=True,
|
||||
examples: List[InputExample], label_list: List[str], max_length: int, tokenizer: PreTrainedTokenizer,
|
||||
) -> List[InputFeatures]:
|
||||
"""
|
||||
Loads a data file into a list of `InputFeatures`
|
||||
|
||||
@@ -319,7 +319,6 @@ def evaluate(args, model, tokenizer, prefix=""):
|
||||
outputs = model(**inputs)
|
||||
|
||||
for i, feature_index in enumerate(feature_indices):
|
||||
# TODO: i and feature_index are the same number! Simplify by removing enumerate?
|
||||
eval_feature = features[feature_index.item()]
|
||||
unique_id = int(eval_feature.unique_id)
|
||||
|
||||
|
||||
@@ -5,5 +5,11 @@ psutil
|
||||
sacrebleu
|
||||
rouge-score
|
||||
tensorflow_datasets
|
||||
pytorch-lightning==0.7.3 # April 10, 2020 release
|
||||
pytorch-lightning==0.7.6
|
||||
matplotlib
|
||||
git-python==1.0.3
|
||||
faiss
|
||||
streamlit
|
||||
elasticsearch
|
||||
pandas
|
||||
nlp
|
||||
@@ -1,47 +1,70 @@
|
||||
### Get CNN Data
|
||||
To be able to reproduce the authors' results on the CNN/Daily Mail dataset you first need to download both CNN and Daily Mail datasets [from Kyunghyun Cho's website](https://cs.nyu.edu/~kcho/DMQA/) (the links next to "Stories") in the same folder. Then uncompress the archives by running:
|
||||
### Data
|
||||
|
||||
CNN/DailyMail data
|
||||
```bash
|
||||
cd examples/summarization
|
||||
wget https://s3.amazonaws.com/datasets.huggingface.co/summarization/cnn_dm.tgz
|
||||
tar -xzvf cnn_dm.tgz
|
||||
export CNN_DIR=${PWD}/cnn_dm
|
||||
```
|
||||
|
||||
this should make a directory called cnn_dm/ with files like `test.source`.
|
||||
To use your own data, copy that files format. Each article to be summarized is on its own line.
|
||||
|
||||
XSUM Data:
|
||||
```bash
|
||||
cd examples/summarization
|
||||
wget https://s3.amazonaws.com/datasets.huggingface.co/summarization/xsum.tar.gz
|
||||
tar -xzvf xsum.tar.gz
|
||||
export XSUM_DIR=${PWD}/xsum
|
||||
```
|
||||
|
||||
|
||||
### Evaluation
|
||||
|
||||
To create summaries for each article in dataset, run:
|
||||
```bash
|
||||
python evaluate_cnn.py <path_to_test.source> test_generations.txt <model-name> --score_path rouge_scores.txt
|
||||
python run_eval.py <path_to_test.source> test_generations.txt <model-name> --score_path rouge_scores.txt
|
||||
```
|
||||
The default batch size, 8, fits in 16GB GPU memory, but may need to be adjusted to fit your system.
|
||||
The default batch size, 4, fits in 16GB GPU memory, but may need to be adjusted to fit your system.
|
||||
|
||||
|
||||
### Training
|
||||
Run/modify `finetune_bart.sh` or `finetune_t5.sh`
|
||||
Run/modify `finetune.sh`
|
||||
|
||||
### Stanford CoreNLP Setup
|
||||
The following command should work on a 16GB GPU:
|
||||
```bash
|
||||
export me=`git config user.name`
|
||||
./finetune.sh \
|
||||
--data_dir $XSUM_DIR \
|
||||
--train_batch_size=1 \
|
||||
--eval_batch_size=1 \
|
||||
--output_dir="$me"_xsum_results \
|
||||
--num_train_epochs 1
|
||||
```
|
||||
ptb_tokenize () {
|
||||
cat $1 | java edu.stanford.nlp.process.PTBTokenizer -ioFileList -preserveLines > $2
|
||||
}
|
||||
|
||||
sudo apt install openjdk-8-jre-headless
|
||||
sudo apt-get install ant
|
||||
wget http://nlp.stanford.edu/software/stanford-corenlp-full-2018-10-05.zip
|
||||
unzip stanford-corenlp-full-2018-10-05.zip
|
||||
cd stanford-corenlp-full-2018-10-05
|
||||
export CLASSPATH=stanford-corenlp-3.9.2.jar:stanford-corenlp-3.9.2-models.jar
|
||||
```
|
||||
Then run `ptb_tokenize` on `test.target` and your generated hypotheses.
|
||||
### Rouge Setup
|
||||
Install `files2rouge` following the instructions at [here](https://github.com/pltrdy/files2rouge).
|
||||
I also needed to run `sudo apt-get install libxml-parser-perl`
|
||||
Tips:
|
||||
- 1 epoch at batch size 1 for bart-large takes 24 hours, requires 13GB GPU RAM with fp16 on an NVIDIA-V100.
|
||||
- try `bart-base`, `--freeze_encoder` or `--freeze_embeds` for faster training/larger batch size. (3hr/epoch with bs=8, see below)
|
||||
- `fp16_opt_level=O1` (the default works best).
|
||||
- If you are finetuning on your own dataset, start from `bart-large-cnn` if you want long summaries and `bart-large-xsum` if you want short summaries.
|
||||
(It rarely makes sense to start from `bart-large` unless you are a researching finetuning methods).
|
||||
- In addition to the pytorch-lightning .ckpt checkpoint, a transformers checkpoint will be saved.
|
||||
Load it with `BartForConditionalGeneration.from_pretrained(f'{output_dir}/best_tfmr)`.
|
||||
- At the moment, `--do_predict` does not work in a multi-gpu setting. You need to use `evaluate_checkpoint` or the `run_eval.py` code.
|
||||
- If you want to run experiments on improving the summarization finetuning process, try the XSUM Shared Task (below). It's faster to train than CNNDM because the summaries are shorter.
|
||||
|
||||
```python
|
||||
from files2rouge import files2rouge
|
||||
from files2rouge import settings
|
||||
files2rouge.run(<path_to_tokenized_hypo>,
|
||||
<path_to_tokenized_target>,
|
||||
saveto='rouge_output.txt')
|
||||
### XSUM Shared Task
|
||||
Compare XSUM results with others by using `--logger wandb_shared`. This requires `wandb` registration.
|
||||
Here is an example command
|
||||
```bash
|
||||
export me=`git config user.name`
|
||||
./finetune.sh \
|
||||
--data_dir $XSUM_DIR \
|
||||
--output_dir "$me"_xsum_frozen_embs \
|
||||
--logger wandb_shared \
|
||||
--train_batch_size 16 --eval_batch_size 16 --freeze_embeds --freeze_encoder \
|
||||
--num_train_epochs 6
|
||||
```
|
||||
|
||||
Results can be viewed [here](https://app.wandb.ai/sshleifer/hf_summarization/table?workspace=user-)
|
||||
@@ -0,0 +1,85 @@
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pytorch_lightning as pl
|
||||
import torch
|
||||
from pytorch_lightning.callbacks import ModelCheckpoint
|
||||
from pytorch_lightning.utilities import rank_zero_only
|
||||
|
||||
|
||||
def count_trainable_parameters(model):
|
||||
model_parameters = filter(lambda p: p.requires_grad, model.parameters())
|
||||
params = sum([np.prod(p.size()) for p in model_parameters])
|
||||
return params
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Seq2SeqLoggingCallback(pl.Callback):
|
||||
def _write_logs(
|
||||
self, trainer: pl.Trainer, pl_module: pl.LightningModule, type_path: str, save_generations=True
|
||||
) -> None:
|
||||
logger.info(f"***** {type_path} results at step {trainer.global_step:05d} *****")
|
||||
if not pl_module.is_logger():
|
||||
return
|
||||
metrics = trainer.callback_metrics
|
||||
trainer.logger.log_metrics({k: v for k, v in metrics.items() if k not in ["log", "progress_bar", "preds"]})
|
||||
# Log results
|
||||
od = Path(pl_module.hparams.output_dir)
|
||||
if type_path == "test":
|
||||
results_file = od / "test_results.txt"
|
||||
generations_file = od / "test_generations.txt"
|
||||
else:
|
||||
results_file = od / f"{type_path}_results_{trainer.global_step:05d}.txt"
|
||||
generations_file = od / f"{type_path}_generations_{trainer.global_step:05d}.txt"
|
||||
|
||||
with open(results_file, "a+") as writer:
|
||||
for key in sorted(metrics):
|
||||
if key in ["log", "progress_bar", "preds"]:
|
||||
continue
|
||||
val = metrics[key]
|
||||
if isinstance(val, torch.Tensor):
|
||||
val = val.item()
|
||||
msg = f"{key}: {val:.6f}\n"
|
||||
writer.write(msg)
|
||||
|
||||
if not save_generations:
|
||||
return
|
||||
|
||||
if "preds" in metrics:
|
||||
content = "\n".join(metrics["preds"])
|
||||
generations_file.open("w+").write(content)
|
||||
|
||||
@rank_zero_only
|
||||
def on_train_start(self, trainer, pl_module):
|
||||
try:
|
||||
npars = pl_module.model.model.num_parameters()
|
||||
except AttributeError:
|
||||
npars = pl_module.model.num_parameters()
|
||||
|
||||
n_trainable_pars = count_trainable_parameters(pl_module)
|
||||
# mp stands for million parameters
|
||||
trainer.logger.log_metrics({"n_params": npars, "mp": npars / 1e6, "grad_mp": n_trainable_pars / 1e6})
|
||||
|
||||
@rank_zero_only
|
||||
def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule):
|
||||
return self._write_logs(trainer, pl_module, "val")
|
||||
|
||||
@rank_zero_only
|
||||
def on_test_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule):
|
||||
return self._write_logs(trainer, pl_module, "test")
|
||||
|
||||
|
||||
def get_rouge2_checkpoint_callback(output_dir):
|
||||
"""Saves the best model by validation ROUGE2 score."""
|
||||
checkpoint_callback = ModelCheckpoint(
|
||||
filepath=os.path.join(output_dir, "{val_avg_rouge2:.4f}-{step_count}"),
|
||||
monitor="val_rouge",
|
||||
mode="max",
|
||||
save_top_k=1,
|
||||
period=0, # maybe save a checkpoint every time val is run, not just end of epoch.
|
||||
)
|
||||
return checkpoint_callback
|
||||
@@ -0,0 +1,448 @@
|
||||
import argparse
|
||||
import gc
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
import pytorch_lightning as pl
|
||||
import torch
|
||||
from torch import nn
|
||||
from torch.nn import functional as F
|
||||
|
||||
from lightning_base import generic_train
|
||||
from transformers import AdamW, BartConfig, BartForConditionalGeneration, T5Config, T5ForConditionalGeneration
|
||||
|
||||
|
||||
try:
|
||||
from .finetune import SummarizationModule
|
||||
from .initialization_utils import init_student, copy_layers
|
||||
from .utils import (
|
||||
use_task_specific_params,
|
||||
SummarizationDataset,
|
||||
pickle_load,
|
||||
freeze_params,
|
||||
assert_all_frozen,
|
||||
any_requires_grad,
|
||||
)
|
||||
from .finetune import main as ft_main
|
||||
except ImportError:
|
||||
from finetune import SummarizationModule
|
||||
from finetune import main as ft_main
|
||||
from initialization_utils import init_student, copy_layers
|
||||
from utils import (
|
||||
use_task_specific_params,
|
||||
SummarizationDataset,
|
||||
pickle_load,
|
||||
freeze_params,
|
||||
assert_all_frozen,
|
||||
any_requires_grad,
|
||||
)
|
||||
|
||||
|
||||
class SummarizationDistiller(SummarizationModule):
|
||||
loss_names = ["loss", "ce_loss", "mlm_loss", "enc_mse_loss", "hid_loss_enc", "hid_loss_dec"]
|
||||
|
||||
def __init__(self, hparams):
|
||||
assert Path(hparams.data_dir).exists()
|
||||
|
||||
d_layers_to_copy, student, student_cfg, teacher = self.pre_init(hparams)
|
||||
|
||||
super().__init__(hparams, model=student, config=student_cfg)
|
||||
self.teacher = teacher
|
||||
use_task_specific_params(self.teacher, "summarization")
|
||||
freeze_params(self.teacher)
|
||||
self.sanity_check_gradients()
|
||||
self.ce_loss_fct = nn.KLDivLoss(reduction="batchmean")
|
||||
self.temperature = 2.0
|
||||
self.alpha_mlm = hparams.alpha_mlm
|
||||
self.alpha_ce = hparams.alpha_ce
|
||||
self.alpha_hid = hparams.alpha_hid
|
||||
# self.alpha_cos = hparams.alpha_cos
|
||||
self.alpha_encoder_loss = self.hparams.alpha_encoder_loss
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
def sanity_check_gradients(self):
|
||||
assert_all_frozen(self.teacher)
|
||||
assert_all_frozen(self.model.model.decoder.embed_tokens)
|
||||
assert_all_frozen(self.model.model.encoder.embed_tokens)
|
||||
if self.different_encoder:
|
||||
assert any_requires_grad(self.model.model.encoder)
|
||||
else:
|
||||
freeze_params(self.model.model.encoder)
|
||||
del self.teacher.model.encoder
|
||||
|
||||
def pre_init(self, hparams):
|
||||
# Dump empty student model at a path, then call from_pretrained on it
|
||||
teacher = BartForConditionalGeneration.from_pretrained(hparams.teacher).eval()
|
||||
student_updates = {
|
||||
"decoder_layers": hparams.student_decoder_layers,
|
||||
"encoder_layers": hparams.student_encoder_layers,
|
||||
}
|
||||
d_layers_to_copy = get_layers_to_copy(student_updates["decoder_layers"], teacher.config.decoder_layers)
|
||||
e_layers_to_copy: List = get_layers_to_copy(student_updates["encoder_layers"], teacher.config.encoder_layers)
|
||||
hparams.d_layer_to_copy = d_layers_to_copy
|
||||
hparams.e_layer_to_copy = e_layers_to_copy
|
||||
kw = teacher.config.to_diff_dict()
|
||||
kw.update(student_updates)
|
||||
# Copy weights
|
||||
student_cfg = BartConfig(**kw)
|
||||
student = BartForConditionalGeneration(student_cfg)
|
||||
student, _ = init_student(student, teacher)
|
||||
self.copy_to_student(d_layers_to_copy, e_layers_to_copy, hparams, student, teacher)
|
||||
Path(hparams.output_dir).mkdir(exist_ok=True)
|
||||
return d_layers_to_copy, student, student_cfg, teacher
|
||||
|
||||
def copy_to_student(self, d_layers_to_copy, e_layers_to_copy, hparams, student, teacher):
|
||||
if teacher.config.model_type == "t5":
|
||||
return self.copy_t5_to_student(d_layers_to_copy, e_layers_to_copy, hparams, student, teacher)
|
||||
self.different_encoder: bool = hparams.student_encoder_layers != teacher.config.encoder_layers
|
||||
self.different_decoder = hparams.student_decoder_layers != teacher.config.decoder_layers
|
||||
if self.different_decoder:
|
||||
copy_layers(teacher.model.decoder.layers, student.model.decoder.layers, d_layers_to_copy)
|
||||
if self.different_encoder:
|
||||
copy_layers(teacher.model.encoder.layers, student.model.encoder.layers, e_layers_to_copy)
|
||||
|
||||
def copy_t5_to_student(self, d_layers_to_copy, e_layers_to_copy, hparams, student, teacher):
|
||||
self.different_encoder: bool = hparams.student_encoder_layers != teacher.config.num_layers
|
||||
self.different_decoder = hparams.student_decoder_layers != teacher.config.num_layers
|
||||
if self.different_decoder:
|
||||
copy_layers(teacher.decoder.block, student.decoder.block, d_layers_to_copy)
|
||||
if self.different_encoder:
|
||||
copy_layers(teacher.encoder.block, student.encoder.block, e_layers_to_copy)
|
||||
|
||||
def get_dataset(self, type_path) -> SummarizationDataset:
|
||||
n_obs = self.n_obs[type_path]
|
||||
dataset = SummarizationDataset(self.tokenizer, type_path=type_path, n_obs=n_obs, **self.dataset_kwargs)
|
||||
return dataset
|
||||
|
||||
def calc_mse_loss(self, teacher_outputs: torch.Tensor, student_outputs: torch.Tensor, mask) -> torch.FloatTensor:
|
||||
if mask is not None:
|
||||
# mask has False at padding_idx
|
||||
sel_mask = mask[:, :, None].expand_as(student_outputs).bool()
|
||||
s_logits_slct = torch.masked_select(student_outputs, sel_mask)
|
||||
t_logits_slct = torch.masked_select(teacher_outputs, sel_mask)
|
||||
else:
|
||||
t_logits_slct = teacher_outputs
|
||||
s_logits_slct = student_outputs
|
||||
return F.mse_loss(s_logits_slct, t_logits_slct)
|
||||
|
||||
def calc_ce_loss(self, mask, s_logits, t_logits):
|
||||
if mask is not None:
|
||||
# mask has False at padding_idx
|
||||
sel_mask = mask[:, :, None].expand_as(s_logits)
|
||||
s_logits_slct = torch.masked_select(
|
||||
s_logits, sel_mask
|
||||
) # (bs * seq_length * voc_size) modulo the 1s in mask
|
||||
t_logits_slct = torch.masked_select(
|
||||
t_logits, sel_mask
|
||||
) # (bs * seq_length * voc_size) modulo the 1s in mask
|
||||
else:
|
||||
t_logits_slct = t_logits
|
||||
s_logits_slct = s_logits # (bs * seq_length * voc_size) modulo the 1s in mask
|
||||
s_logits_slct = s_logits_slct.view(-1, s_logits.size(-1)) # (bs * seq_length, voc_size) modulo the 1s in mask
|
||||
t_logits_slct = t_logits_slct.view(-1, s_logits.size(-1)) # (bs * seq_length, voc_size) modulo the 1s in mask
|
||||
assert t_logits_slct.size() == s_logits_slct.size()
|
||||
loss_ce = (
|
||||
self.ce_loss_fct(
|
||||
F.log_softmax(s_logits_slct / self.temperature, dim=-1),
|
||||
F.softmax(t_logits_slct / self.temperature, dim=-1),
|
||||
)
|
||||
* (self.temperature) ** 2
|
||||
)
|
||||
return loss_ce, s_logits_slct, t_logits_slct
|
||||
|
||||
def configure_optimizers(self):
|
||||
"Prepare optimizer and schedule (linear warmup and decay)"
|
||||
|
||||
model = self.model
|
||||
no_decay = ["bias", "LayerNorm.weight"]
|
||||
optimizer_grouped_parameters = [
|
||||
{
|
||||
"params": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)],
|
||||
"weight_decay": self.hparams.weight_decay,
|
||||
},
|
||||
{
|
||||
"params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)],
|
||||
"weight_decay": 0.0,
|
||||
},
|
||||
]
|
||||
optimizer = AdamW(optimizer_grouped_parameters, lr=self.hparams.learning_rate, eps=self.hparams.adam_epsilon)
|
||||
self.opt = optimizer
|
||||
return [optimizer]
|
||||
|
||||
@staticmethod
|
||||
def add_model_specific_args(parser, root_dir):
|
||||
SummarizationModule.add_model_specific_args(parser, root_dir)
|
||||
parser.add_argument("--teacher", default="facebook/bart-large-cnn", type=str)
|
||||
parser.add_argument("--alpha_ce", default=0.8, type=float)
|
||||
parser.add_argument("--alpha_mlm", default=0.2, type=float)
|
||||
# parser.add_argument("--alpha_cos", default=0.0, type=float)
|
||||
parser.add_argument("--alpha_encoder_loss", default=0.0, type=float)
|
||||
parser.add_argument("--alpha_hid", default=0.0, type=float, required=False)
|
||||
parser.add_argument(
|
||||
"--student_decoder_layers", default=12, type=int, required=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--student_encoder_layers", default=12, type=int, required=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no_teacher", action="store_true", default=False,
|
||||
)
|
||||
parser.add_argument( # TODO: remove
|
||||
"--enc_only", action="store_true", default=False,
|
||||
)
|
||||
return parser
|
||||
|
||||
def _step(self, batch):
|
||||
# assert is_frozen(self.teacher)
|
||||
pad_token_id = self.tokenizer.pad_token_id
|
||||
input_ids, src_mask, y = batch["input_ids"], batch["attention_mask"], batch["decoder_input_ids"]
|
||||
decoder_input_ids = y[:, :-1].contiguous()
|
||||
labels = y[:, 1:].clone()
|
||||
labels[y[:, 1:] == pad_token_id] = -100
|
||||
# noinspection PyCallingNonCallable
|
||||
sloss, slogits, dec_hidden, enc_outputs, enc_hidden_state = self(
|
||||
input_ids,
|
||||
attention_mask=src_mask,
|
||||
decoder_input_ids=decoder_input_ids,
|
||||
labels=labels,
|
||||
output_hidden_states=True,
|
||||
output_attentions=False,
|
||||
)
|
||||
|
||||
def zero_tensor():
|
||||
return torch.tensor(0.0).type_as(sloss)
|
||||
|
||||
loss_encoder, hid_loss_enc, hid_loss_dec = zero_tensor(), zero_tensor(), zero_tensor()
|
||||
if self.different_encoder:
|
||||
with torch.no_grad():
|
||||
teacher_enc_outputs, teacher_enc_hid, _ = self.teacher.model.encoder(
|
||||
input_ids, attention_mask=src_mask, output_hidden_states=True
|
||||
)
|
||||
if self.hparams.alpha_encoder_loss > 0:
|
||||
loss_encoder = self.calc_mse_loss(enc_outputs, teacher_enc_outputs, src_mask)
|
||||
|
||||
hid_loss_enc = self.calc_hidden_loss(
|
||||
src_mask, enc_hidden_state, teacher_enc_hid, self.hparams.e_layer_to_copy
|
||||
)
|
||||
|
||||
teacher_enc_outputs = (enc_outputs,)
|
||||
assert isinstance(teacher_enc_outputs, tuple), type(teacher_enc_outputs)
|
||||
|
||||
with torch.no_grad():
|
||||
tloss, tlogits, tdec_hidden, _ = self.teacher(
|
||||
input_ids,
|
||||
attention_mask=src_mask,
|
||||
encoder_outputs=teacher_enc_outputs,
|
||||
decoder_input_ids=decoder_input_ids,
|
||||
lm_labels=labels,
|
||||
output_hidden_states=True,
|
||||
)
|
||||
dec_mask = decoder_input_ids.ne(pad_token_id)
|
||||
loss_ce, s_logits_slct, t_logits_slct = self.calc_ce_loss(dec_mask, slogits, tlogits)
|
||||
if self.alpha_hid > 0:
|
||||
hid_loss_dec = self.calc_hidden_loss(dec_mask, dec_hidden, tdec_hidden, self.hparams.d_layer_to_copy)
|
||||
|
||||
blended_loss = (
|
||||
self.alpha_ce * loss_ce
|
||||
+ self.alpha_mlm * sloss
|
||||
+ self.hparams.alpha_encoder_loss * loss_encoder
|
||||
+ self.hparams.alpha_hid * (hid_loss_enc + hid_loss_dec)
|
||||
)
|
||||
return blended_loss, loss_ce, sloss, loss_encoder, hid_loss_enc, hid_loss_dec
|
||||
|
||||
def calc_hidden_loss(self, attention_mask, hidden_states, hidden_states_T, matches):
|
||||
assert not isinstance(
|
||||
hidden_states, torch.Tensor
|
||||
), f"expected list or tuple for hidden_states, got tensor of shape {hidden_states.shape}"
|
||||
assert not isinstance(
|
||||
hidden_states_T, torch.Tensor
|
||||
), f"expected list or tuple for hidden_states_T, got tensor of shape {hidden_states_T.shape}"
|
||||
mask = attention_mask.to(hidden_states[0])
|
||||
valid_count = mask.sum() * hidden_states[0].size(-1)
|
||||
hidden_losses = [
|
||||
(F.mse_loss(hidden_states[i], hidden_states_T[j], reduction="none") * mask.unsqueeze(-1)).sum()
|
||||
/ valid_count
|
||||
for i, j in enumerate(matches)
|
||||
]
|
||||
return sum(hidden_losses)
|
||||
|
||||
|
||||
class T5SummarizationDistiller(SummarizationDistiller):
|
||||
def pre_init(self, hparams):
|
||||
teacher = T5ForConditionalGeneration.from_pretrained(hparams.teacher)
|
||||
n_layer = hparams.student_decoder_layers
|
||||
assert n_layer == hparams.student_encoder_layers # TODO(SS): relax this
|
||||
d_layers_to_copy = get_layers_to_copy(n_layer, len(teacher.decoder.block))
|
||||
e_layers_to_copy: List = get_layers_to_copy(n_layer, len(teacher.encoder.block))
|
||||
student_updates = {"num_layers": n_layer}
|
||||
hparams.d_layer_to_copy = d_layers_to_copy
|
||||
hparams.e_layer_to_copy = e_layers_to_copy
|
||||
kw = teacher.config.to_diff_dict()
|
||||
|
||||
kw.update(student_updates)
|
||||
# Copy weights
|
||||
student_cfg = T5Config(**kw)
|
||||
student = T5ForConditionalGeneration(student_cfg)
|
||||
student, _ = init_student(student, teacher)
|
||||
self.copy_to_student(d_layers_to_copy, e_layers_to_copy, hparams, student, teacher)
|
||||
Path(hparams.output_dir).mkdir(exist_ok=True)
|
||||
task_specific_params = student.config.task_specific_params
|
||||
if task_specific_params is not None:
|
||||
student.config.update(task_specific_params.get("summarization", {}))
|
||||
return d_layers_to_copy, student, student_cfg, teacher
|
||||
|
||||
def freeze_embeds(self):
|
||||
freeze_params(self.model.shared)
|
||||
for d in [self.model.encoder, self.model.decoder]:
|
||||
freeze_params(d.embed_tokens)
|
||||
|
||||
def sanity_check_gradients(self):
|
||||
"""T5"""
|
||||
assert_all_frozen(self.teacher)
|
||||
assert_all_frozen(self.model.decoder.embed_tokens)
|
||||
assert_all_frozen(self.model.encoder.embed_tokens)
|
||||
if self.different_encoder:
|
||||
assert any_requires_grad(self.model.encoder)
|
||||
else:
|
||||
freeze_params(self.model.encoder)
|
||||
del self.teacher.model.encoder
|
||||
if self.different_decoder:
|
||||
assert any_requires_grad(self.model.decoder)
|
||||
else:
|
||||
freeze_params(self.model.decoder) # TODO(SS): very suspicious
|
||||
|
||||
def _step(self, batch):
|
||||
pad_token_id = self.tokenizer.pad_token_id
|
||||
source_ids, source_mask, y = batch["input_ids"], batch["attention_mask"], batch["decoder_input_ids"]
|
||||
decoder_input_ids = y[:, :-1].contiguous()
|
||||
labels = y[:, 1:].clone()
|
||||
labels[y[:, 1:] == pad_token_id] = -100
|
||||
# noinspection PyCallingNonCallable
|
||||
dec_mask = decoder_input_ids.ne(pad_token_id)
|
||||
|
||||
sloss, slogits, dec_hidden, enc_outputs, enc_hidden_state = self(
|
||||
source_ids,
|
||||
attention_mask=source_mask,
|
||||
decoder_input_ids=decoder_input_ids,
|
||||
labels=labels,
|
||||
output_hidden_states=True,
|
||||
output_attentions=False,
|
||||
use_cache=False,
|
||||
)
|
||||
|
||||
def zero_tensor():
|
||||
return torch.tensor(0.0).type_as(sloss)
|
||||
|
||||
loss_encoder, hid_loss_enc, hid_loss_dec = zero_tensor(), zero_tensor(), zero_tensor()
|
||||
if self.different_encoder:
|
||||
with torch.no_grad():
|
||||
teacher_enc_outputs, teacher_enc_hid = self.teacher.encoder(
|
||||
source_ids, attention_mask=source_mask, output_hidden_states=True, use_cache=False,
|
||||
)
|
||||
if self.hparams.alpha_encoder_loss > 0:
|
||||
loss_encoder = self.calc_mse_loss(enc_outputs, teacher_enc_outputs, source_mask)
|
||||
|
||||
hid_loss_enc = self.calc_hidden_loss(
|
||||
source_mask, enc_hidden_state, teacher_enc_hid, self.hparams.e_layer_to_copy
|
||||
)
|
||||
|
||||
teacher_enc_outputs = (enc_outputs,)
|
||||
assert isinstance(teacher_enc_outputs, tuple), type(teacher_enc_outputs)
|
||||
|
||||
with torch.no_grad():
|
||||
tloss, tlogits, tdec_hidden, _ = self.teacher(
|
||||
source_ids,
|
||||
attention_mask=source_mask,
|
||||
encoder_outputs=teacher_enc_outputs,
|
||||
decoder_input_ids=decoder_input_ids,
|
||||
lm_labels=labels,
|
||||
output_hidden_states=True,
|
||||
use_cache=False,
|
||||
)
|
||||
|
||||
loss_ce, s_logits_slct, t_logits_slct = self.calc_ce_loss(dec_mask, slogits, tlogits)
|
||||
if self.alpha_hid > 0:
|
||||
hid_loss_dec = self.calc_hidden_loss(dec_mask, dec_hidden, tdec_hidden, self.hparams.d_layer_to_copy)
|
||||
|
||||
blended_loss = (
|
||||
self.alpha_ce * loss_ce
|
||||
+ self.alpha_mlm * sloss
|
||||
+ self.hparams.alpha_encoder_loss * loss_encoder
|
||||
+ self.hparams.alpha_hid * (hid_loss_enc + hid_loss_dec)
|
||||
)
|
||||
return blended_loss, loss_ce, sloss, loss_encoder, hid_loss_enc, hid_loss_dec
|
||||
|
||||
|
||||
def create_module(args):
|
||||
t5 = "t5" in args.model_name_or_path
|
||||
if args.no_teacher:
|
||||
assert not args.enc_only
|
||||
module_cls = SummarizationModule
|
||||
elif t5:
|
||||
module_cls = T5SummarizationDistiller
|
||||
elif args.enc_only:
|
||||
raise ValueError("Deleted that")
|
||||
else:
|
||||
module_cls = SummarizationDistiller
|
||||
args.setup_cls: str = module_cls.__name__
|
||||
model = module_cls(args)
|
||||
return model
|
||||
|
||||
|
||||
def evaluate_checkpoint(ckpt_path: Path, dest_dir=None):
|
||||
exp_dir = ckpt_path.parent
|
||||
if dest_dir is None:
|
||||
dest_dir = exp_dir
|
||||
clash = list(dest_dir.glob("test_generations*"))
|
||||
if clash:
|
||||
print(f"SKIPPING to avoid overwriting {clash}")
|
||||
ckpt = torch.load(ckpt_path, map_location="cpu")
|
||||
if "hparams" in ckpt:
|
||||
args = argparse.Namespace(**ckpt["hparams"])
|
||||
else:
|
||||
args = argparse.Namespace(**pickle_load(exp_dir / "hparams.pkl"))
|
||||
args.resume_from_checkpoint = str(ckpt_path)
|
||||
args.do_train = False
|
||||
args.output_dir = str(dest_dir)
|
||||
args.n_gpu = 1
|
||||
args.eval_batch_size = 16
|
||||
Path(args.output_dir).mkdir(exist_ok=True)
|
||||
model = create_module(args)
|
||||
trainer: pl.Trainer = generic_train(model, args, early_stopping_callback=False)
|
||||
trainer.test(model)
|
||||
|
||||
|
||||
def get_layers_to_copy(n_to_get, tot):
|
||||
all_layers = list(range(tot))
|
||||
if tot == 12: # Alternating for special cases
|
||||
layers_to_copy = { # maps # layers in student -> which teacher layers to copy
|
||||
6: [0, 2, 4, 7, 9, 11],
|
||||
1: [11],
|
||||
3: [0, 6, 11],
|
||||
2: [0, 11],
|
||||
4: [0, 4, 8, 11],
|
||||
9: [0, 1, 2, 4, 5, 7, 9, 10, 11],
|
||||
12: all_layers,
|
||||
}
|
||||
return layers_to_copy[n_to_get]
|
||||
else:
|
||||
return all_layers[:n_to_get]
|
||||
|
||||
|
||||
def distill_main(args):
|
||||
Path(args.output_dir).mkdir(exist_ok=True)
|
||||
if len(os.listdir(args.output_dir)) > 3 and args.do_train:
|
||||
raise ValueError("Output directory ({}) already exists and is not empty.".format(args.output_dir))
|
||||
|
||||
model = create_module(args)
|
||||
return ft_main(args, model=model)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser = SummarizationDistiller.add_model_specific_args(parser, os.getcwd())
|
||||
args = parser.parse_args()
|
||||
|
||||
distill_main(args)
|
||||
@@ -1,100 +0,0 @@
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
import torch
|
||||
from rouge_score import rouge_scorer, scoring
|
||||
from tqdm import tqdm
|
||||
|
||||
from transformers import AutoModelWithLMHead, AutoTokenizer
|
||||
|
||||
|
||||
DEFAULT_DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
||||
|
||||
|
||||
def chunks(lst, n):
|
||||
"""Yield successive n-sized chunks from lst."""
|
||||
for i in range(0, len(lst), n):
|
||||
yield lst[i : i + n]
|
||||
|
||||
|
||||
def generate_summaries(
|
||||
examples: list, out_file: str, model_name: str, batch_size: int = 8, device: str = DEFAULT_DEVICE
|
||||
):
|
||||
fout = Path(out_file).open("w", encoding="utf-8")
|
||||
model = AutoModelWithLMHead.from_pretrained(model_name).to(device)
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
||||
|
||||
# update config with summarization specific params
|
||||
task_specific_params = model.config.task_specific_params
|
||||
if task_specific_params is not None:
|
||||
model.config.update(task_specific_params.get("summarization", {}))
|
||||
|
||||
for batch in tqdm(list(chunks(examples, batch_size))):
|
||||
if "t5" in model_name:
|
||||
batch = [model.config.prefix + text for text in batch]
|
||||
dct = tokenizer.batch_encode_plus(batch, max_length=1024, return_tensors="pt", pad_to_max_length=True).to(
|
||||
device
|
||||
)
|
||||
summaries = model.generate(**dct)
|
||||
|
||||
dec = tokenizer.batch_decode(summaries, skip_special_tokens=True, clean_up_tokenization_spaces=False)
|
||||
for hypothesis in dec:
|
||||
fout.write(hypothesis + "\n")
|
||||
fout.flush()
|
||||
|
||||
|
||||
def calculate_rouge(output_lns, reference_lns, score_path):
|
||||
score_file = Path(score_path).open("w")
|
||||
scorer = rouge_scorer.RougeScorer(["rouge1", "rouge2", "rougeL"], use_stemmer=True)
|
||||
aggregator = scoring.BootstrapAggregator()
|
||||
|
||||
for reference_ln, output_ln in zip(reference_lns, output_lns):
|
||||
scores = scorer.score(reference_ln, output_ln)
|
||||
aggregator.add_scores(scores)
|
||||
|
||||
result = aggregator.aggregate()
|
||||
score_file.write(
|
||||
"ROUGE_1: \n{} \n\n ROUGE_2: \n{} \n\n ROUGE_L: \n{} \n\n".format(
|
||||
result["rouge1"], result["rouge2"], result["rougeL"]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def run_generate():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"input_path", type=str, help="like cnn_dm/test.source or cnn_dm/test_articles_input.txt",
|
||||
)
|
||||
parser.add_argument(
|
||||
"output_path", type=str, help="where to save summaries",
|
||||
)
|
||||
parser.add_argument(
|
||||
"model_name",
|
||||
type=str,
|
||||
default="facebook/bart-large-cnn",
|
||||
help="like bart-large-cnn,'t5-small', 't5-base', 't5-large', 't5-3b', 't5-11b",
|
||||
)
|
||||
parser.add_argument("--reference_path", type=str, required=False, help="like cnn_dm/test_reference_summaries.txt")
|
||||
parser.add_argument(
|
||||
"--score_path", type=str, required=False, help="where to save the rouge score",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--device", type=str, required=False, default=DEFAULT_DEVICE, help="cuda, cuda:1, cpu etc.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--bs", type=int, default=8, required=False, help="batch size: how many to summarize at a time",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
examples = [" " + x.rstrip() if "t5" in args.model_name else x.rstrip() for x in open(args.input_path).readlines()]
|
||||
|
||||
generate_summaries(examples, args.output_path, args.model_name, batch_size=args.bs, device=args.device)
|
||||
if args.score_path is not None:
|
||||
output_lns = [x.rstrip() for x in open(args.output_path).readlines()]
|
||||
reference_lns = [x.rstrip() for x in open(args.reference_path).readlines()]
|
||||
|
||||
calculate_rouge(output_lns, reference_lns, args.score_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_generate()
|
||||
@@ -3,91 +3,169 @@ import glob
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
import numpy as np
|
||||
import pytorch_lightning as pl
|
||||
import torch
|
||||
from torch.utils.data import DataLoader
|
||||
|
||||
from lightning_base import BaseTransformer, add_generic_args, generic_train, get_linear_schedule_with_warmup
|
||||
from lightning_base import BaseTransformer, add_generic_args, generic_train
|
||||
from transformers import get_linear_schedule_with_warmup
|
||||
|
||||
|
||||
try:
|
||||
from .utils import SummarizationDataset
|
||||
from .utils import (
|
||||
use_task_specific_params,
|
||||
SummarizationDataset,
|
||||
lmap,
|
||||
flatten_list,
|
||||
pickle_save,
|
||||
save_git_info,
|
||||
freeze_params,
|
||||
calculate_rouge,
|
||||
get_git_info,
|
||||
ROUGE_KEYS,
|
||||
)
|
||||
from .callbacks import Seq2SeqLoggingCallback, get_rouge2_checkpoint_callback
|
||||
except ImportError:
|
||||
from utils import SummarizationDataset
|
||||
|
||||
from utils import (
|
||||
use_task_specific_params,
|
||||
SummarizationDataset,
|
||||
lmap,
|
||||
flatten_list,
|
||||
pickle_save,
|
||||
save_git_info,
|
||||
freeze_params,
|
||||
calculate_rouge,
|
||||
get_git_info,
|
||||
ROUGE_KEYS,
|
||||
)
|
||||
from callbacks import Seq2SeqLoggingCallback, get_rouge2_checkpoint_callback
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SummarizationTrainer(BaseTransformer):
|
||||
class SummarizationModule(BaseTransformer):
|
||||
mode = "summarization"
|
||||
loss_names = ["loss"]
|
||||
|
||||
mode = "language-modeling"
|
||||
def __init__(self, hparams, **kwargs):
|
||||
super().__init__(hparams, num_labels=None, mode=self.mode, **kwargs)
|
||||
use_task_specific_params(self.model, "summarization")
|
||||
save_git_info(self.hparams.output_dir)
|
||||
self.metrics_save_path = Path(self.output_dir) / "metrics.pkl"
|
||||
self.hparams_save_path = Path(self.output_dir) / "hparams.pkl"
|
||||
self.step_count = 0
|
||||
self.metrics = {"train": [], "val": [], "test": []}
|
||||
|
||||
def __init__(self, hparams):
|
||||
super().__init__(hparams, num_labels=None, mode=self.mode)
|
||||
self.dataset_kwargs: dict = dict(
|
||||
data_dir=self.hparams.data_dir,
|
||||
max_source_length=self.hparams.max_source_length,
|
||||
max_target_length=self.hparams.max_target_length,
|
||||
prefix=self.model.config.prefix or "",
|
||||
)
|
||||
n_observations_per_split = {
|
||||
"train": self.hparams.n_train,
|
||||
"val": self.hparams.n_val,
|
||||
"test": self.hparams.n_test,
|
||||
}
|
||||
self.n_obs = {k: v if v >= 0 else None for k, v in n_observations_per_split.items()}
|
||||
|
||||
def forward(self, input_ids, attention_mask=None, decoder_input_ids=None, lm_labels=None):
|
||||
return self.model(
|
||||
input_ids, attention_mask=attention_mask, decoder_input_ids=decoder_input_ids, lm_labels=lm_labels,
|
||||
self.target_lens = {
|
||||
"train": self.hparams.max_target_length,
|
||||
"val": self.hparams.val_max_target_length,
|
||||
"test": self.hparams.test_max_target_length,
|
||||
}
|
||||
assert self.target_lens["train"] <= self.target_lens["val"], f"target_lens: {self.target_lens}"
|
||||
assert self.target_lens["train"] <= self.target_lens["test"], f"target_lens: {self.target_lens}"
|
||||
|
||||
if self.hparams.freeze_embeds:
|
||||
self.freeze_embeds()
|
||||
if self.hparams.freeze_encoder:
|
||||
freeze_params(self.model.model.encoder) # TODO: this will break for t5
|
||||
self.hparams.git_sha = get_git_info()["repo_sha"]
|
||||
self.num_workers = 4 if self.hparams.gpus <= 1 else None # passing num_workers breaks lightning for multigpu
|
||||
|
||||
def freeze_embeds(self):
|
||||
"""Freeze token embeddings and positional embeddings for bart, just token embeddings for t5."""
|
||||
if self.model.config.model_type == "bart":
|
||||
freeze_params(self.model.model.shared)
|
||||
for d in [self.model.model.encoder, self.model.model.decoder]:
|
||||
freeze_params(d.embed_positions)
|
||||
freeze_params(d.embed_tokens)
|
||||
else:
|
||||
freeze_params(self.model.shared)
|
||||
for d in [self.model.encoder, self.model.decoder]:
|
||||
freeze_params(d.embed_tokens)
|
||||
|
||||
def forward(self, input_ids, **kwargs):
|
||||
return self.model(input_ids, **kwargs)
|
||||
|
||||
def ids_to_clean_text(self, generated_ids: List[int]):
|
||||
gen_text = self.tokenizer.batch_decode(
|
||||
generated_ids, skip_special_tokens=True, clean_up_tokenization_spaces=True
|
||||
)
|
||||
return lmap(str.strip, gen_text)
|
||||
|
||||
def _step(self, batch):
|
||||
def _step(self, batch: dict) -> Tuple:
|
||||
pad_token_id = self.tokenizer.pad_token_id
|
||||
source_ids, source_mask, y = batch["source_ids"], batch["source_mask"], batch["target_ids"]
|
||||
source_ids, source_mask, y = batch["input_ids"], batch["attention_mask"], batch["decoder_input_ids"]
|
||||
y_ids = y[:, :-1].contiguous()
|
||||
lm_labels = y[:, 1:].clone()
|
||||
lm_labels[y[:, 1:] == pad_token_id] = -100
|
||||
outputs = self(source_ids, attention_mask=source_mask, decoder_input_ids=y_ids, lm_labels=lm_labels,)
|
||||
|
||||
outputs = self(source_ids, attention_mask=source_mask, decoder_input_ids=y_ids, labels=lm_labels,)
|
||||
loss = outputs[0]
|
||||
return (loss,)
|
||||
|
||||
return loss
|
||||
def training_step(self, batch, batch_idx) -> Dict:
|
||||
loss_tensors = self._step(batch)
|
||||
logs = {name: loss for name, loss in zip(self.loss_names, loss_tensors)}
|
||||
return {"loss": loss_tensors[0], "log": logs}
|
||||
|
||||
def training_step(self, batch, batch_idx):
|
||||
loss = self._step(batch)
|
||||
def validation_step(self, batch, batch_idx) -> Dict:
|
||||
return self._generative_step(batch)
|
||||
|
||||
tensorboard_logs = {"train_loss": loss}
|
||||
return {"loss": loss, "log": tensorboard_logs}
|
||||
def validation_end(self, outputs, prefix="val") -> Dict:
|
||||
self.step_count += 1
|
||||
losses = {k: torch.stack([x[k] for x in outputs]).mean() for k in self.loss_names}
|
||||
loss = losses["loss"]
|
||||
rouges = {k: np.array([x[k] for x in outputs]).mean() for k in ROUGE_KEYS + ["gen_time", "summ_len"]}
|
||||
rouge_tensor: torch.FloatTensor = torch.tensor(rouges["rouge2"]).type_as(loss)
|
||||
rouges.update({k: v.item() for k, v in losses.items()})
|
||||
losses.update(rouges)
|
||||
metrics = {f"{prefix}_avg_{k}": x for k, x in losses.items()}
|
||||
metrics["step_count"] = self.step_count
|
||||
self.save_metrics(metrics, prefix) # writes to self.metrics_save_path
|
||||
preds = flatten_list([x["preds"] for x in outputs])
|
||||
return {"log": metrics, "preds": preds, f"{prefix}_loss": loss, f"{prefix}_rouge": rouge_tensor}
|
||||
|
||||
def validation_step(self, batch, batch_idx):
|
||||
loss = self._step(batch)
|
||||
return {"val_loss": loss}
|
||||
def save_metrics(self, metrics, prefix) -> None:
|
||||
self.metrics[prefix].append(metrics)
|
||||
pickle_save(self.metrics, self.metrics_save_path)
|
||||
|
||||
def validation_end(self, outputs):
|
||||
avg_loss = torch.stack([x["val_loss"] for x in outputs]).mean()
|
||||
tensorboard_logs = {"val_loss": avg_loss}
|
||||
return {"avg_val_loss": avg_loss, "log": tensorboard_logs}
|
||||
|
||||
def test_step(self, batch, batch_idx):
|
||||
def _generative_step(self, batch):
|
||||
pad_token_id = self.tokenizer.pad_token_id
|
||||
source_ids, source_mask, y = SummarizationDataset.trim_seq2seq_batch(batch, pad_token_id)
|
||||
# NOTE: the following kwargs get more speed and lower quality summaries than those in evaluate_cnn.py
|
||||
generated_ids = self.model.generate(
|
||||
input_ids=source_ids,
|
||||
attention_mask=source_mask,
|
||||
num_beams=1,
|
||||
max_length=80,
|
||||
repetition_penalty=2.5,
|
||||
length_penalty=1.0,
|
||||
early_stopping=True,
|
||||
use_cache=True,
|
||||
)
|
||||
preds = [
|
||||
self.tokenizer.decode(g, skip_special_tokens=True, clean_up_tokenization_spaces=True)
|
||||
for g in generated_ids
|
||||
]
|
||||
target = [self.tokenizer.decode(t, skip_special_tokens=True, clean_up_tokenization_spaces=True) for t in y]
|
||||
loss = self._step(batch)
|
||||
# TODO(SS): task specific params
|
||||
|
||||
return {"val_loss": loss, "preds": preds, "target": target}
|
||||
t0 = time.time()
|
||||
generated_ids = self.model.generate(input_ids=source_ids, attention_mask=source_mask, use_cache=True,)
|
||||
gen_time = time.time() - t0
|
||||
preds = self.ids_to_clean_text(generated_ids)
|
||||
target = self.ids_to_clean_text(y)
|
||||
loss_tensors = self._step(batch)
|
||||
base_metrics = {name: loss for name, loss in zip(self.loss_names, loss_tensors)}
|
||||
rouge: Dict = calculate_rouge(preds, target)
|
||||
summ_len = np.mean(lmap(len, generated_ids))
|
||||
base_metrics.update(gen_time=gen_time, summ_len=summ_len, preds=preds, target=target, **rouge)
|
||||
return base_metrics
|
||||
|
||||
def test_step(self, batch, batch_idx):
|
||||
return self._generative_step(batch)
|
||||
|
||||
def test_end(self, outputs):
|
||||
return self.validation_end(outputs)
|
||||
return self.validation_end(outputs, prefix="test")
|
||||
|
||||
def test_epoch_end(self, outputs):
|
||||
output_test_predictions_file = os.path.join(self.hparams.output_dir, "test_predictions.txt")
|
||||
@@ -102,15 +180,43 @@ class SummarizationTrainer(BaseTransformer):
|
||||
|
||||
return self.test_end(outputs)
|
||||
|
||||
def validation_epoch_end(self, outputs):
|
||||
self.validation_end(outputs, "val")
|
||||
|
||||
def get_dataset(self, type_path) -> SummarizationDataset:
|
||||
n_obs = self.n_obs[type_path]
|
||||
max_target_length = self.target_lens[type_path]
|
||||
dataset = SummarizationDataset(
|
||||
self.tokenizer,
|
||||
type_path=type_path,
|
||||
n_obs=n_obs,
|
||||
max_target_length=max_target_length,
|
||||
**self.dataset_kwargs,
|
||||
)
|
||||
return dataset
|
||||
|
||||
def get_dataloader(self, type_path: str, batch_size: int, shuffle: bool = False) -> DataLoader:
|
||||
dataset = SummarizationDataset(self.tokenizer, type_path=type_path, **self.dataset_kwargs)
|
||||
dataloader = DataLoader(dataset, batch_size=batch_size, collate_fn=dataset.collate_fn, shuffle=shuffle)
|
||||
dataset = self.get_dataset(type_path)
|
||||
sampler = None
|
||||
if self.hparams.sortish_sampler and type_path == "train":
|
||||
assert self.hparams.gpus <= 1 # TODO: assert earlier
|
||||
sampler = dataset.make_sortish_sampler(batch_size)
|
||||
shuffle = False
|
||||
|
||||
dataloader = DataLoader(
|
||||
dataset,
|
||||
batch_size=batch_size,
|
||||
collate_fn=dataset.collate_fn,
|
||||
shuffle=shuffle,
|
||||
num_workers=self.num_workers,
|
||||
sampler=sampler,
|
||||
)
|
||||
return dataloader
|
||||
|
||||
def train_dataloader(self) -> DataLoader:
|
||||
dataloader = self.get_dataloader("train", batch_size=self.hparams.train_batch_size, shuffle=True)
|
||||
t_total = (
|
||||
(len(dataloader.dataset) // (self.hparams.train_batch_size * max(1, self.hparams.n_gpu)))
|
||||
(len(dataloader.dataset) // (self.hparams.train_batch_size * max(1, self.hparams.gpus)))
|
||||
// self.hparams.gradient_accumulation_steps
|
||||
* float(self.hparams.num_train_epochs)
|
||||
)
|
||||
@@ -129,7 +235,7 @@ class SummarizationTrainer(BaseTransformer):
|
||||
@staticmethod
|
||||
def add_model_specific_args(parser, root_dir):
|
||||
BaseTransformer.add_model_specific_args(parser, root_dir)
|
||||
# Add BART specific options
|
||||
add_generic_args(parser, root_dir)
|
||||
parser.add_argument(
|
||||
"--max_source_length",
|
||||
default=1024,
|
||||
@@ -144,41 +250,82 @@ class SummarizationTrainer(BaseTransformer):
|
||||
help="The maximum total input sequence length after tokenization. Sequences longer "
|
||||
"than this will be truncated, sequences shorter will be padded.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--val_max_target_length",
|
||||
default=142, # these defaults are optimized for CNNDM. For xsum, see README.md.
|
||||
type=int,
|
||||
help="The maximum total input sequence length after tokenization. Sequences longer "
|
||||
"than this will be truncated, sequences shorter will be padded.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--test_max_target_length",
|
||||
default=142,
|
||||
type=int,
|
||||
help="The maximum total input sequence length after tokenization. Sequences longer "
|
||||
"than this will be truncated, sequences shorter will be padded.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--data_dir",
|
||||
default=None,
|
||||
type=str,
|
||||
required=True,
|
||||
help="The input data dir. Should contain the dataset files for the CNN/DM summarization task.",
|
||||
help="The input data dir. Should contain train.source, train.target, val.source, val.target, test.source, test.target",
|
||||
)
|
||||
parser.add_argument("--freeze_encoder", action="store_true")
|
||||
parser.add_argument("--freeze_embeds", action="store_true")
|
||||
parser.add_argument("--sortish_sampler", action="store_true", default=False)
|
||||
parser.add_argument("--logger", type=str, choices=["default", "wandb", "wandb_shared"], default="default")
|
||||
parser.add_argument("--n_train", type=int, default=-1, required=False, help="# examples. -1 means use all.")
|
||||
parser.add_argument("--n_val", type=int, default=500, required=False, help="# examples. -1 means use all.")
|
||||
parser.add_argument("--n_test", type=int, default=-1, required=False, help="# examples. -1 means use all.")
|
||||
return parser
|
||||
|
||||
|
||||
def main(args):
|
||||
def main(args, model=None) -> SummarizationModule:
|
||||
Path(args.output_dir).mkdir(exist_ok=True)
|
||||
if len(os.listdir(args.output_dir)) > 3 and args.do_train:
|
||||
raise ValueError("Output directory ({}) already exists and is not empty.".format(args.output_dir))
|
||||
if model is None:
|
||||
model: BaseTransformer = SummarizationModule(args)
|
||||
if (
|
||||
args.logger == "default"
|
||||
or args.fast_dev_run
|
||||
or str(args.output_dir).startswith("/tmp")
|
||||
or str(args.output_dir).startswith("/var")
|
||||
):
|
||||
logger = True # don't pollute wandb logs unnecessarily
|
||||
elif args.logger == "wandb":
|
||||
from pytorch_lightning.loggers import WandbLogger
|
||||
|
||||
# If output_dir not provided, a folder will be generated in pwd
|
||||
if not args.output_dir:
|
||||
args.output_dir = os.path.join("./results", f"{args.task}_{time.strftime('%Y%m%d_%H%M%S')}",)
|
||||
os.makedirs(args.output_dir)
|
||||
model = SummarizationTrainer(args)
|
||||
trainer = generic_train(model, args)
|
||||
logger = WandbLogger(name=model.output_dir.name)
|
||||
elif args.logger == "wandb_shared":
|
||||
from pytorch_lightning.loggers import WandbLogger
|
||||
|
||||
# Optionally, predict on dev set and write to output_dir
|
||||
if args.do_predict:
|
||||
# See https://github.com/huggingface/transformers/issues/3159
|
||||
# pl use this format to create a checkpoint:
|
||||
# https://github.com/PyTorchLightning/pytorch-lightning/blob/master\
|
||||
# /pytorch_lightning/callbacks/model_checkpoint.py#L169
|
||||
checkpoints = list(sorted(glob.glob(os.path.join(args.output_dir, "checkpointepoch=*.ckpt"), recursive=True)))
|
||||
model = model.load_from_checkpoint(checkpoints[-1])
|
||||
trainer.test(model)
|
||||
# TODO: separate LB for CNN, we should use Path(args.data_dir).name to determine the correct LB.
|
||||
logger = WandbLogger(name=model.output_dir.name, project="hf_summarization")
|
||||
trainer: pl.Trainer = generic_train(
|
||||
model,
|
||||
args,
|
||||
logging_callback=Seq2SeqLoggingCallback(),
|
||||
checkpoint_callback=get_rouge2_checkpoint_callback(args.output_dir),
|
||||
logger=logger,
|
||||
# TODO: early stopping callback seems messed up
|
||||
)
|
||||
if not args.do_predict:
|
||||
return model
|
||||
|
||||
model.hparams.test_checkpoint = ""
|
||||
checkpoints = list(sorted(glob.glob(os.path.join(args.output_dir, "*.ckpt"), recursive=True)))
|
||||
if checkpoints:
|
||||
model.hparams.test_checkpoint = checkpoints[-1]
|
||||
trainer.resume_from_checkpoint = checkpoints[-1]
|
||||
trainer.logger.log_hyperparams(model.hparams)
|
||||
trainer.test(model) # this breaks in DDP, known lightning issue. See evaluate_checkpoint to recover metrics.
|
||||
return model
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
add_generic_args(parser, os.getcwd())
|
||||
parser = SummarizationTrainer.add_model_specific_args(parser, os.getcwd())
|
||||
parser = SummarizationModule.add_model_specific_args(parser, os.getcwd())
|
||||
args = parser.parse_args()
|
||||
|
||||
main(args)
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
export OUTPUT_DIR=bart_cnn_finetune
|
||||
|
||||
# Make output directory if it doesn't exist
|
||||
mkdir -p $OUTPUT_DIR
|
||||
|
||||
# Add parent directory to python path to access lightning_base.py
|
||||
export PYTHONPATH="../":"${PYTHONPATH}"
|
||||
|
||||
|
||||
# --model_name_or_path=t5-base for t5
|
||||
|
||||
python finetune.py \
|
||||
--model_name_or_path=facebook/bart-large \
|
||||
--learning_rate=3e-5 \
|
||||
--fp16 \
|
||||
--gpus 1 \
|
||||
--do_train \
|
||||
--do_predict \
|
||||
--n_val 1000 \
|
||||
--val_check_interval 0.1 \
|
||||
--sortish_sampler \
|
||||
--max_target_length=56 \
|
||||
$@
|
||||
@@ -1,18 +0,0 @@
|
||||
export OUTPUT_DIR_NAME=bart_sum
|
||||
export CURRENT_DIR=${PWD}
|
||||
export OUTPUT_DIR=${CURRENT_DIR}/${OUTPUT_DIR_NAME}
|
||||
|
||||
# Make output directory if it doesn't exist
|
||||
mkdir -p $OUTPUT_DIR
|
||||
|
||||
# Add parent directory to python path to access lightning_base.py
|
||||
export PYTHONPATH="../":"${PYTHONPATH}"
|
||||
|
||||
python finetune.py \
|
||||
--data_dir=./cnn-dailymail/cnn_dm \
|
||||
--model_name_or_path=bart-large \
|
||||
--learning_rate=3e-5 \
|
||||
--train_batch_size=4 \
|
||||
--eval_batch_size=4 \
|
||||
--output_dir=$OUTPUT_DIR \
|
||||
--do_train $@
|
||||
@@ -0,0 +1,20 @@
|
||||
from typing import List
|
||||
|
||||
from torch import nn
|
||||
|
||||
|
||||
def init_student(student, teacher):
|
||||
teacher_state_dict = teacher.state_dict()
|
||||
info = student.load_state_dict(teacher_state_dict, strict=False)
|
||||
assert info.missing_keys == [], info.missing_keys
|
||||
return student, info
|
||||
|
||||
|
||||
def copy_decoder_layers(teacher, student, l2copy=[0, 2, 4, 7, 9, 11]):
|
||||
copy_layers(teacher.model.decoder.layers, student.model.decoder.layers, l2copy)
|
||||
|
||||
|
||||
def copy_layers(teacher_layers: nn.ModuleList, student_layers: nn.ModuleList, layers_to_copy: List) -> None:
|
||||
layers_to_copy = nn.ModuleList([l for i, l in enumerate(teacher_layers) if i in layers_to_copy])
|
||||
assert len(student_layers) == len(layers_to_copy), f"{len(student_layers)} != {len(layers_to_copy)}"
|
||||
student_layers.load_state_dict(layers_to_copy.state_dict())
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#CNN_DIR = /home/shleifer/transformers_fork/examples/summarization/bart/cnn_dm
|
||||
|
||||
# Add parent directory to python path to access lightning_base.py
|
||||
export PYTHONPATH="../":"${PYTHONPATH}"
|
||||
|
||||
python distillation.py \
|
||||
--learning_rate=3e-4 \
|
||||
--do_train \
|
||||
--do_predict \
|
||||
--fp16 \
|
||||
--val_check_interval 0.1 \
|
||||
$@
|
||||
@@ -0,0 +1,78 @@
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import torch
|
||||
from tqdm import tqdm
|
||||
|
||||
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
|
||||
|
||||
|
||||
try:
|
||||
from .finetune import calculate_rouge, use_task_specific_params
|
||||
except ImportError:
|
||||
from finetune import calculate_rouge, use_task_specific_params
|
||||
|
||||
DEFAULT_DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
||||
|
||||
|
||||
def chunks(lst, n):
|
||||
"""Yield successive n-sized chunks from lst."""
|
||||
for i in range(0, len(lst), n):
|
||||
yield lst[i : i + n]
|
||||
|
||||
|
||||
def generate_summaries(
|
||||
examples: list, out_file: str, model_name: str, batch_size: int = 8, device: str = DEFAULT_DEVICE, fp16=False,
|
||||
) -> None:
|
||||
fout = Path(out_file).open("w", encoding="utf-8")
|
||||
model = AutoModelForSeq2SeqLM.from_pretrained(model_name).to(device)
|
||||
if fp16:
|
||||
model = model.half()
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
||||
|
||||
# update config with summarization specific params
|
||||
use_task_specific_params(model, "summarization")
|
||||
|
||||
for batch in tqdm(list(chunks(examples, batch_size))):
|
||||
if "t5" in model_name:
|
||||
batch = [model.config.prefix + text for text in batch]
|
||||
dct = tokenizer.batch_encode_plus(batch, max_length=1024, return_tensors="pt", pad_to_max_length=True).to(
|
||||
device
|
||||
)
|
||||
summaries = model.generate(**dct)
|
||||
|
||||
dec = tokenizer.batch_decode(summaries, skip_special_tokens=True, clean_up_tokenization_spaces=False)
|
||||
for hypothesis in dec:
|
||||
fout.write(hypothesis + "\n")
|
||||
fout.flush()
|
||||
|
||||
|
||||
def run_generate():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("input_path", type=str, help="like cnn_dm/test.source")
|
||||
parser.add_argument("output_path", type=str, help="where to save summaries")
|
||||
parser.add_argument("model_name", type=str, help="like facebook/bart-large-cnn,t5-base, etc.")
|
||||
parser.add_argument("--reference_path", type=str, required=False, help="like cnn_dm/test_reference_summaries.txt")
|
||||
parser.add_argument("--score_path", type=str, required=False, help="where to save the rouge score in json format")
|
||||
parser.add_argument("--device", type=str, required=False, default=DEFAULT_DEVICE, help="cuda, cuda:1, cpu etc.")
|
||||
parser.add_argument("--bs", type=int, default=8, required=False, help="batch size")
|
||||
parser.add_argument("--fp16", action="store_true")
|
||||
args = parser.parse_args()
|
||||
examples = [" " + x.rstrip() if "t5" in args.model_name else x.rstrip() for x in open(args.input_path).readlines()]
|
||||
|
||||
generate_summaries(
|
||||
examples, args.output_path, args.model_name, batch_size=args.bs, device=args.device, fp16=args.fp16
|
||||
)
|
||||
if args.score_path is not None:
|
||||
output_lns = [x.rstrip() for x in open(args.output_path).readlines()]
|
||||
reference_lns = [x.rstrip() for x in open(args.reference_path).readlines()]
|
||||
|
||||
rouge: dict = calculate_rouge(output_lns, reference_lns)
|
||||
|
||||
json.dump(rouge, open("score_path", "w+"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_generate()
|
||||
@@ -7,28 +7,40 @@ import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import torch
|
||||
from torch.utils.data import DataLoader
|
||||
|
||||
from transformers import BartTokenizer
|
||||
|
||||
from .evaluate_cnn import run_generate
|
||||
from .distillation import distill_main, evaluate_checkpoint
|
||||
from .finetune import main
|
||||
from .utils import SummarizationDataset
|
||||
from .run_eval import generate_summaries, run_generate
|
||||
from .utils import SummarizationDataset, lmap, pickle_load
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
DEFAULT_ARGS = {
|
||||
FP16_EVER = False
|
||||
CHEAP_ARGS = {
|
||||
"logger": "default",
|
||||
"alpha_hid": 0,
|
||||
"freeze_embeds": True,
|
||||
"enc_only": False,
|
||||
"tgt_suffix": "",
|
||||
"resume_from_checkpoint": None,
|
||||
"sortish_sampler": True,
|
||||
"student_decoder_layers": 1,
|
||||
"val_check_interval": 1.0,
|
||||
"output_dir": "",
|
||||
"fp16": False,
|
||||
"no_teacher": False,
|
||||
"fp16_opt_level": "O1",
|
||||
"n_gpu": 1,
|
||||
"gpus": 1 if torch.cuda.is_available() else 0,
|
||||
"n_tpu_cores": 0,
|
||||
"max_grad_norm": 1.0,
|
||||
"do_train": True,
|
||||
"do_predict": False,
|
||||
"do_predict": True,
|
||||
"gradient_accumulation_steps": 1,
|
||||
"server_ip": "",
|
||||
"server_port": "",
|
||||
@@ -36,7 +48,7 @@ DEFAULT_ARGS = {
|
||||
"model_type": "bart",
|
||||
"model_name_or_path": "sshleifer/bart-tiny-random",
|
||||
"config_name": "",
|
||||
"tokenizer_name": "",
|
||||
"tokenizer_name": "facebook/bart-large",
|
||||
"cache_dir": "",
|
||||
"do_lower_case": False,
|
||||
"learning_rate": 3e-05,
|
||||
@@ -48,6 +60,17 @@ DEFAULT_ARGS = {
|
||||
"eval_batch_size": 2,
|
||||
"max_source_length": 12,
|
||||
"max_target_length": 12,
|
||||
"val_max_target_length": 12,
|
||||
"test_max_target_length": 12,
|
||||
"fast_dev_run": False,
|
||||
"no_cache": False,
|
||||
"n_train": -1,
|
||||
"n_val": -1,
|
||||
"n_test": -1,
|
||||
"student_encoder_layers": 1,
|
||||
"alpha_loss_encoder": 0.0,
|
||||
"freeze_encoder": False,
|
||||
"auto_scale_batch_size": False,
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +79,9 @@ def _dump_articles(path: Path, articles: list):
|
||||
f.write("\n".join(articles))
|
||||
|
||||
|
||||
BDIR = Path("~/transformers_fork/examples/summarization/bart/").absolute()
|
||||
|
||||
|
||||
def make_test_data_dir():
|
||||
tmp_dir = Path(tempfile.gettempdir())
|
||||
articles = [" Sam ate lunch today", "Sams lunch ingredients"]
|
||||
@@ -66,6 +92,169 @@ def make_test_data_dir():
|
||||
return tmp_dir
|
||||
|
||||
|
||||
@unittest.skip("These wont' pass until hidden_states kwarg is merged.")
|
||||
class TestSummarizationDistiller(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
logging.disable(logging.CRITICAL) # remove noisy download output from tracebacks
|
||||
return cls
|
||||
|
||||
@unittest.skipUnless(torch.cuda.device_count() > 1, "skipping multiGPU test")
|
||||
def test_bdc_multigpu(self):
|
||||
updates = dict(
|
||||
student_encoder_layers=2,
|
||||
student_decoder_layers=1,
|
||||
no_teacher=True,
|
||||
freeze_encoder=True,
|
||||
gpus=2,
|
||||
sortish_sampler=False,
|
||||
)
|
||||
self._bart_distiller_cli(updates)
|
||||
|
||||
@unittest.skipUnless(torch.cuda.is_available(), "skipping fp16 test")
|
||||
def test_bdc_fp16(self):
|
||||
updates = dict(
|
||||
student_encoder_layers=2,
|
||||
student_decoder_layers=1,
|
||||
alpha_hid=3.0,
|
||||
freeze_encoder=True,
|
||||
gpus=1,
|
||||
fp16=FP16_EVER,
|
||||
fp16_opt_level="O1",
|
||||
)
|
||||
self._bart_distiller_cli(updates)
|
||||
|
||||
@unittest.skipUnless(torch.cuda.is_available(), "skipping fp16 test")
|
||||
def test_bdc_t5_eval_fp16(self):
|
||||
updates = dict(
|
||||
fp16=FP16_EVER,
|
||||
gpus=1,
|
||||
model_type="t5",
|
||||
model_name_or_path="patrickvonplaten/t5-tiny-random",
|
||||
do_train=False,
|
||||
do_predict=True,
|
||||
tokenizer_name=None,
|
||||
no_teacher=True,
|
||||
)
|
||||
self._bart_distiller_cli(updates, check_contents=False)
|
||||
|
||||
@unittest.skipUnless(torch.cuda.is_available(), "skipping fp16 test")
|
||||
def test_bdc_t5_train_fp16(self):
|
||||
updates = dict(
|
||||
fp16=FP16_EVER,
|
||||
gpus=1,
|
||||
model_type="t5",
|
||||
model_name_or_path="patrickvonplaten/t5-tiny-random",
|
||||
do_train=True,
|
||||
do_predict=True,
|
||||
tokenizer_name="patrickvonplaten/t5-tiny-random",
|
||||
no_teacher=True,
|
||||
)
|
||||
self._bart_distiller_cli(updates)
|
||||
|
||||
def test_bdc_no_teacher(self):
|
||||
updates = dict(student_encoder_layers=2, student_decoder_layers=1, no_teacher=True,)
|
||||
self._bart_distiller_cli(updates)
|
||||
|
||||
def test_bdc_yes_teacher(self):
|
||||
updates = dict(student_encoder_layers=2, student_decoder_layers=1,)
|
||||
self._bart_distiller_cli(updates)
|
||||
|
||||
def test_bdc_checkpointing(self):
|
||||
|
||||
updates = dict(
|
||||
student_encoder_layers=2,
|
||||
student_decoder_layers=1,
|
||||
num_train_epochs=4,
|
||||
val_check_interval=0.25,
|
||||
alpha_hid=2.0,
|
||||
)
|
||||
model = self._bart_distiller_cli(updates, check_contents=False)
|
||||
|
||||
ckpts = list(Path(model.output_dir).glob("*.ckpt"))
|
||||
self.assertEqual(1, len(ckpts))
|
||||
transformer_ckpts = list(Path(model.output_dir).glob("**/*.bin"))
|
||||
self.assertEqual(len(transformer_ckpts), len(ckpts))
|
||||
new_transformer_ckpts = list(Path(model.output_dir).glob("**/*.bin"))
|
||||
self.assertEqual(len(new_transformer_ckpts), 1)
|
||||
examples = lmap(str.strip, model.hparams.data_dir.joinpath("test.source").open().readlines())
|
||||
out_path = tempfile.mktemp()
|
||||
generate_summaries(examples, out_path, new_transformer_ckpts[0].parent)
|
||||
self.assertTrue(Path(out_path).exists())
|
||||
|
||||
evaluate_checkpoint(ckpts[0], dest_dir=Path(tempfile.mkdtemp()))
|
||||
|
||||
def test_bdc_t5(self):
|
||||
updates = dict(
|
||||
student_encoder_layers=1,
|
||||
student_decoder_layers=1,
|
||||
alpha_hid=2.0,
|
||||
teacher="patrickvonplaten/t5-tiny-random",
|
||||
model_type="t5",
|
||||
model_name_or_path="patrickvonplaten/t5-tiny-random",
|
||||
tokenizer_name="patrickvonplaten/t5-tiny-random",
|
||||
)
|
||||
self._bart_distiller_cli(updates)
|
||||
|
||||
def test_bdc_t5_eval(self):
|
||||
updates = dict(
|
||||
model_type="t5",
|
||||
model_name_or_path="patrickvonplaten/t5-tiny-random",
|
||||
do_train=False,
|
||||
do_predict=True,
|
||||
tokenizer_name="patrickvonplaten/t5-tiny-random",
|
||||
no_teacher=True,
|
||||
)
|
||||
self._bart_distiller_cli(updates, check_contents=False)
|
||||
|
||||
def _bart_distiller_cli(self, updates, check_contents=True):
|
||||
default_updates = dict(
|
||||
model_type="bart",
|
||||
train_batch_size=1,
|
||||
eval_batch_size=2,
|
||||
num_train_epochs=2,
|
||||
alpha_mlm=0.2,
|
||||
alpha_ce=0.8,
|
||||
do_predict=True,
|
||||
gpus=1 if torch.cuda.is_available() else 0,
|
||||
model_name_or_path="sshleifer/tinier_bart",
|
||||
teacher=CHEAP_ARGS["model_name_or_path"],
|
||||
val_check_interval=0.5,
|
||||
alpha_encoder_loss=0.4,
|
||||
)
|
||||
default_updates.update(updates)
|
||||
args_d: dict = CHEAP_ARGS.copy()
|
||||
tmp_dir = make_test_data_dir()
|
||||
output_dir = tempfile.mkdtemp(prefix="output_")
|
||||
|
||||
args_d.update(data_dir=tmp_dir, output_dir=output_dir, **default_updates)
|
||||
model = distill_main(argparse.Namespace(**args_d))
|
||||
if not check_contents:
|
||||
return model
|
||||
contents = os.listdir(output_dir)
|
||||
ckpt_name = "val_avg_rouge2=0.0000-step_count=2.ckpt" # "val_avg_rouge2=0.0000-epoch=1.ckpt" # "epoch=1-val_avg_rouge2=0.0000.ckpt"
|
||||
contents = {os.path.basename(p) for p in contents}
|
||||
self.assertIn(ckpt_name, contents)
|
||||
self.assertIn("metrics.pkl", contents)
|
||||
self.assertIn("test_generations.txt", contents)
|
||||
self.assertIn("val_generations_1.txt", contents)
|
||||
self.assertIn("val_1_results.txt", contents)
|
||||
self.assertIn("test_results.txt", contents)
|
||||
# self.assertEqual(len(contents), 15)
|
||||
|
||||
metrics = pickle_load(Path(output_dir) / "metrics.pkl")
|
||||
import pandas as pd
|
||||
|
||||
val_df = pd.DataFrame(metrics["val"])
|
||||
train_df = pd.DataFrame(metrics["train"])
|
||||
test_df = pd.DataFrame(metrics["test"])
|
||||
desired_n_evals = args_d["num_train_epochs"] * 2 + 1
|
||||
self.assertEqual(val_df.shape[0], desired_n_evals) #
|
||||
self.assertEqual(test_df.shape[1], val_df.shape[1])
|
||||
self.assertEqual(train_df.shape[0], 0)
|
||||
return model
|
||||
|
||||
|
||||
class TestBartExamples(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@@ -79,49 +268,31 @@ class TestBartExamples(unittest.TestCase):
|
||||
output_file_name = Path(tempfile.gettempdir()) / "utest_output_bart_sum.hypo"
|
||||
articles = [" New York (CNN)When Liana Barrientos was 23 years old, she got married in Westchester County."]
|
||||
_dump_articles(tmp, articles)
|
||||
testargs = ["evaluate_cnn.py", str(tmp), str(output_file_name), "sshleifer/bart-tiny-random"]
|
||||
testargs = ["run_eval.py", str(tmp), str(output_file_name), "sshleifer/bart-tiny-random"]
|
||||
with patch.object(sys, "argv", testargs):
|
||||
run_generate()
|
||||
self.assertTrue(Path(output_file_name).exists())
|
||||
os.remove(Path(output_file_name))
|
||||
|
||||
def test_bart_run_sum_cli(self):
|
||||
args_d: dict = DEFAULT_ARGS.copy()
|
||||
tmp_dir = make_test_data_dir()
|
||||
output_dir = tempfile.mkdtemp(prefix="output_")
|
||||
args_d.update(
|
||||
data_dir=tmp_dir, model_type="bart", train_batch_size=2, eval_batch_size=2, n_gpu=0, output_dir=output_dir,
|
||||
)
|
||||
main(argparse.Namespace(**args_d))
|
||||
args_d.update({"do_train": False, "do_predict": True})
|
||||
|
||||
main(argparse.Namespace(**args_d))
|
||||
contents = os.listdir(output_dir)
|
||||
expected_contents = {
|
||||
"checkpointepoch=0.ckpt",
|
||||
"test_results.txt",
|
||||
}
|
||||
created_files = {os.path.basename(p) for p in contents}
|
||||
self.assertSetEqual(expected_contents, created_files)
|
||||
|
||||
def test_t5_run_sum_cli(self):
|
||||
args_d: dict = DEFAULT_ARGS.copy()
|
||||
args_d: dict = CHEAP_ARGS.copy()
|
||||
|
||||
tmp_dir = make_test_data_dir()
|
||||
output_dir = tempfile.mkdtemp(prefix="output_")
|
||||
args_d.update(
|
||||
data_dir=tmp_dir,
|
||||
model_type="t5",
|
||||
model_name_or_path="patrickvonplaten/t5-tiny-random",
|
||||
tokenizer_name=None, # "patrickvonplaten/t5-tiny-random",
|
||||
train_batch_size=2,
|
||||
eval_batch_size=2,
|
||||
n_gpu=0,
|
||||
gpus=0,
|
||||
output_dir=output_dir,
|
||||
do_predict=True,
|
||||
)
|
||||
main(argparse.Namespace(**args_d))
|
||||
|
||||
# args_d.update({"do_train": False, "do_predict": True})
|
||||
# main(argparse.Namespace(**args_d))
|
||||
assert "n_train" in args_d
|
||||
args = argparse.Namespace(**args_d)
|
||||
main(args)
|
||||
|
||||
def test_bart_summarization_dataset(self):
|
||||
tmp_dir = Path(tempfile.gettempdir())
|
||||
@@ -138,42 +309,16 @@ class TestBartExamples(unittest.TestCase):
|
||||
)
|
||||
dataloader = DataLoader(train_dataset, batch_size=2, collate_fn=train_dataset.collate_fn)
|
||||
for batch in dataloader:
|
||||
self.assertEqual(batch["source_mask"].shape, batch["source_ids"].shape)
|
||||
self.assertEqual(batch["attention_mask"].shape, batch["input_ids"].shape)
|
||||
# show that articles were trimmed.
|
||||
self.assertEqual(batch["source_ids"].shape[1], max_len_source)
|
||||
self.assertGreater(20, batch["source_ids"].shape[1]) # trimmed significantly
|
||||
self.assertEqual(batch["input_ids"].shape[1], max_len_source)
|
||||
self.assertGreater(20, batch["input_ids"].shape[1]) # trimmed significantly
|
||||
|
||||
# show that targets were truncated
|
||||
self.assertEqual(batch["target_ids"].shape[1], trunc_target) # Truncated
|
||||
self.assertEqual(batch["decoder_input_ids"].shape[1], trunc_target) # Truncated
|
||||
self.assertGreater(max_len_target, trunc_target) # Truncated
|
||||
|
||||
|
||||
class TestT5Examples(unittest.TestCase):
|
||||
def test_t5_cli(self):
|
||||
output_file_name = "output_t5_sum.txt"
|
||||
score_file_name = "score_t5_sum.txt"
|
||||
articles = ["New York (CNN)When Liana Barrientos was 23 years old, she got married in Westchester County."]
|
||||
stream_handler = logging.StreamHandler(sys.stdout)
|
||||
logger.addHandler(stream_handler)
|
||||
tmp = Path(tempfile.gettempdir()) / "utest_generations_t5_sum.hypo"
|
||||
with tmp.open("w", encoding="utf-8") as f:
|
||||
f.write("\n".join(articles))
|
||||
|
||||
output_file_name = Path(tempfile.gettempdir()) / "utest_output_t5_sum.hypo"
|
||||
score_file_name = Path(tempfile.gettempdir()) / "utest_score_t5_sum.hypo"
|
||||
|
||||
testargs = [
|
||||
"evaluate_cnn.py",
|
||||
str(tmp),
|
||||
str(output_file_name),
|
||||
"patrickvonplaten/t5-tiny-random",
|
||||
"--reference_path",
|
||||
str(tmp),
|
||||
"--score_path",
|
||||
str(score_file_name),
|
||||
]
|
||||
|
||||
with patch.object(sys, "argv", testargs):
|
||||
run_generate()
|
||||
self.assertTrue(Path(output_file_name).exists())
|
||||
self.assertTrue(Path(score_file_name).exists())
|
||||
def list_to_text_file(lst, path):
|
||||
dest = Path(path)
|
||||
dest.open("w+").writelines(lst)
|
||||
+220
-20
@@ -1,35 +1,113 @@
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
from pathlib import Path
|
||||
from typing import Dict, Iterable, List
|
||||
|
||||
import git
|
||||
import numpy as np
|
||||
import torch
|
||||
from torch.utils.data import Dataset
|
||||
from rouge_score import rouge_scorer, scoring
|
||||
from torch import nn
|
||||
from torch.utils.data import Dataset, Sampler
|
||||
from tqdm import tqdm
|
||||
|
||||
from transformers.tokenization_utils import trim_batch
|
||||
from transformers import BartTokenizer
|
||||
|
||||
|
||||
def encode_file(tokenizer, data_path, max_length, pad_to_max_length=True, return_tensors="pt"):
|
||||
def encode_file(
|
||||
tokenizer,
|
||||
data_path,
|
||||
max_length,
|
||||
pad_to_max_length=True,
|
||||
return_tensors="pt",
|
||||
overwrite_cache=False,
|
||||
prefix="",
|
||||
tok_name="",
|
||||
):
|
||||
cache_path = Path(f"{data_path}_{tok_name}{max_length}.pt")
|
||||
if not overwrite_cache and cache_path.exists():
|
||||
try:
|
||||
examples = torch.load(cache_path)
|
||||
assert isinstance(examples, list)
|
||||
return examples
|
||||
|
||||
except Exception:
|
||||
print(f"failed to load from {cache_path}, retokenizing {data_path}")
|
||||
data_path = Path(data_path)
|
||||
|
||||
lns = lmap(str.strip, data_path.open().readlines())
|
||||
lns = [prefix + text for text in lns]
|
||||
assert lns, f"found empty file at {data_path}"
|
||||
examples = []
|
||||
with open(data_path, "r") as f:
|
||||
for text in f.readlines():
|
||||
tokenized = tokenizer.batch_encode_plus(
|
||||
[text], max_length=max_length, pad_to_max_length=pad_to_max_length, return_tensors=return_tensors,
|
||||
)
|
||||
examples.append(tokenized)
|
||||
for text in tqdm(lns, desc=f"Tokenizing {data_path.name}"):
|
||||
tokenized = tokenizer.batch_encode_plus(
|
||||
[text], # DONT ADD SPACES
|
||||
max_length=max_length,
|
||||
pad_to_max_length=pad_to_max_length,
|
||||
add_prefix_space=True,
|
||||
return_tensors=return_tensors,
|
||||
)
|
||||
examples.append(tokenized)
|
||||
torch.save(lmap(dict, examples), cache_path.open("wb"))
|
||||
return examples
|
||||
|
||||
|
||||
def lmap(f, x):
|
||||
return list(map(f, x))
|
||||
|
||||
|
||||
T5_PREFIX = "summarize: " # HACK, fixme
|
||||
|
||||
|
||||
def trim_batch(
|
||||
input_ids, pad_token_id, attention_mask=None,
|
||||
):
|
||||
"""Remove columns that are populated exclusively by pad_token_id"""
|
||||
keep_column_mask = input_ids.ne(pad_token_id).any(dim=0)
|
||||
if attention_mask is None:
|
||||
return input_ids[:, keep_column_mask]
|
||||
else:
|
||||
return (input_ids[:, keep_column_mask], attention_mask[:, keep_column_mask])
|
||||
|
||||
|
||||
class SummarizationDataset(Dataset):
|
||||
def __init__(
|
||||
self,
|
||||
tokenizer,
|
||||
data_dir="./cnn-dailymail/cnn_dm/",
|
||||
data_dir,
|
||||
type_path="train",
|
||||
max_source_length=1024,
|
||||
max_target_length=56,
|
||||
n_obs=None,
|
||||
overwrite_cache=False,
|
||||
prefix="",
|
||||
):
|
||||
super().__init__()
|
||||
self.tokenizer = tokenizer
|
||||
tok_name = "T5" if not isinstance(tokenizer, BartTokenizer) else ""
|
||||
self.source = encode_file(
|
||||
tokenizer,
|
||||
os.path.join(data_dir, type_path + ".source"),
|
||||
max_source_length,
|
||||
overwrite_cache=overwrite_cache,
|
||||
prefix=prefix,
|
||||
tok_name=tok_name,
|
||||
)
|
||||
if type_path == "train":
|
||||
tgt_path = os.path.join(data_dir, type_path + ".target")
|
||||
else:
|
||||
tgt_path = os.path.join(data_dir, type_path + ".target")
|
||||
|
||||
self.target = encode_file(
|
||||
tokenizer, tgt_path, max_target_length, overwrite_cache=overwrite_cache, tok_name=tok_name
|
||||
)
|
||||
self.source = encode_file(tokenizer, os.path.join(data_dir, type_path + ".source"), max_source_length)
|
||||
self.target = encode_file(tokenizer, os.path.join(data_dir, type_path + ".target"), max_target_length)
|
||||
if n_obs is not None:
|
||||
self.source = self.source[:n_obs]
|
||||
self.target = self.target[:n_obs]
|
||||
self.pad_token_id = tokenizer.pad_token_id
|
||||
|
||||
def __len__(self):
|
||||
return len(self.source)
|
||||
@@ -38,19 +116,141 @@ class SummarizationDataset(Dataset):
|
||||
source_ids = self.source[index]["input_ids"].squeeze()
|
||||
target_ids = self.target[index]["input_ids"].squeeze()
|
||||
src_mask = self.source[index]["attention_mask"].squeeze()
|
||||
return {"source_ids": source_ids, "source_mask": src_mask, "target_ids": target_ids}
|
||||
return {"input_ids": source_ids, "attention_mask": src_mask, "decoder_input_ids": target_ids}
|
||||
|
||||
@staticmethod
|
||||
def trim_seq2seq_batch(batch, pad_token_id):
|
||||
y = trim_batch(batch["target_ids"], pad_token_id)
|
||||
source_ids, source_mask = trim_batch(batch["source_ids"], pad_token_id, attention_mask=batch["source_mask"])
|
||||
y = trim_batch(batch["decoder_input_ids"], pad_token_id)
|
||||
source_ids, source_mask = trim_batch(batch["input_ids"], pad_token_id, attention_mask=batch["attention_mask"])
|
||||
return source_ids, source_mask, y
|
||||
|
||||
def collate_fn(self, batch):
|
||||
input_ids = torch.stack([x["source_ids"] for x in batch])
|
||||
masks = torch.stack([x["source_mask"] for x in batch])
|
||||
target_ids = torch.stack([x["target_ids"] for x in batch])
|
||||
pad_token_id = self.tokenizer.pad_token_id
|
||||
def collate_fn(self, batch) -> dict:
|
||||
input_ids = torch.stack([x["input_ids"] for x in batch])
|
||||
masks = torch.stack([x["attention_mask"] for x in batch])
|
||||
target_ids = torch.stack([x["decoder_input_ids"] for x in batch])
|
||||
pad_token_id = self.pad_token_id
|
||||
y = trim_batch(target_ids, pad_token_id)
|
||||
source_ids, source_mask = trim_batch(input_ids, pad_token_id, attention_mask=masks)
|
||||
return {"source_ids": source_ids, "source_mask": source_mask, "target_ids": y}
|
||||
batch = {"input_ids": source_ids, "attention_mask": source_mask, "decoder_input_ids": y}
|
||||
return batch
|
||||
|
||||
@property
|
||||
def src_lens(self): # Can delete?
|
||||
return lmap(len, self.source)
|
||||
|
||||
@property
|
||||
def tgt_lens(self):
|
||||
return lmap(len, self.target)
|
||||
|
||||
def make_sortish_sampler(self, batch_size):
|
||||
return SortishSampler(self.source, batch_size)
|
||||
|
||||
|
||||
class SortishSampler(Sampler):
|
||||
"Go through the text data by order of src length with a bit of randomness. From fastai repo."
|
||||
|
||||
def __init__(self, data, batch_size):
|
||||
self.data, self.bs = data, batch_size
|
||||
|
||||
def key(self, i):
|
||||
return len(self.data[i])
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.data)
|
||||
|
||||
def __iter__(self):
|
||||
idxs = np.random.permutation(len(self.data))
|
||||
sz = self.bs * 50
|
||||
ck_idx = [idxs[i : i + sz] for i in range(0, len(idxs), sz)]
|
||||
sort_idx = np.concatenate([sorted(s, key=self.key, reverse=True) for s in ck_idx])
|
||||
sz = self.bs
|
||||
ck_idx = [sort_idx[i : i + sz] for i in range(0, len(sort_idx), sz)]
|
||||
max_ck = np.argmax([self.key(ck[0]) for ck in ck_idx]) # find the chunk with the largest key,
|
||||
ck_idx[0], ck_idx[max_ck] = ck_idx[max_ck], ck_idx[0] # then make sure it goes first.
|
||||
sort_idx = np.concatenate(np.random.permutation(ck_idx[1:])) if len(ck_idx) > 1 else np.array([], dtype=np.int)
|
||||
sort_idx = np.concatenate((ck_idx[0], sort_idx))
|
||||
return iter(sort_idx)
|
||||
|
||||
|
||||
def use_task_specific_params(model, task):
|
||||
# update config with summarization specific params
|
||||
task_specific_params = model.config.task_specific_params
|
||||
if task_specific_params is not None:
|
||||
model.config.update(task_specific_params.get(task, {}))
|
||||
|
||||
|
||||
def pickle_load(path):
|
||||
"""pickle.load(path)"""
|
||||
with open(path, "rb") as f:
|
||||
return pickle.load(f)
|
||||
|
||||
|
||||
def pickle_save(obj, path):
|
||||
"""pickle.dump(obj, path)"""
|
||||
with open(path, "wb") as f:
|
||||
return pickle.dump(obj, f)
|
||||
|
||||
|
||||
def flatten_list(summary_ids: List[List]):
|
||||
return [x for x in itertools.chain.from_iterable(summary_ids)]
|
||||
|
||||
|
||||
def save_git_info(folder_path: str):
|
||||
"""
|
||||
Log commit info.
|
||||
"""
|
||||
repo_infos = get_git_info()
|
||||
|
||||
with open(os.path.join(folder_path, "git_log.json"), "w") as f:
|
||||
json.dump(repo_infos, f, indent=4)
|
||||
|
||||
|
||||
def get_git_info():
|
||||
repo = git.Repo(search_parent_directories=True)
|
||||
repo_infos = {
|
||||
"repo_id": str(repo),
|
||||
"repo_sha": str(repo.head.object.hexsha),
|
||||
"repo_branch": str(repo.active_branch),
|
||||
}
|
||||
return repo_infos
|
||||
|
||||
|
||||
ROUGE_KEYS = ["rouge1", "rouge2", "rougeL"]
|
||||
|
||||
|
||||
def calculate_rouge(output_lns: List[str], reference_lns: List[str]) -> Dict:
|
||||
scorer = rouge_scorer.RougeScorer(ROUGE_KEYS, use_stemmer=True)
|
||||
aggregator = scoring.BootstrapAggregator()
|
||||
|
||||
for reference_ln, output_ln in zip(reference_lns, output_lns):
|
||||
scores = scorer.score(reference_ln, output_ln)
|
||||
aggregator.add_scores(scores)
|
||||
|
||||
result = aggregator.aggregate()
|
||||
return {k: v.mid.fmeasure for k, v in result.items()}
|
||||
|
||||
|
||||
def freeze_params(model: nn.Module):
|
||||
for par in model.parameters():
|
||||
par.requires_grad = False
|
||||
|
||||
|
||||
def grad_status(model: nn.Module) -> Iterable:
|
||||
return (par.requires_grad for par in model.parameters())
|
||||
|
||||
|
||||
def any_requires_grad(model: nn.Module) -> bool:
|
||||
return any(grad_status(model))
|
||||
|
||||
|
||||
def assert_all_frozen(model):
|
||||
model_grads: List[bool] = list(grad_status(model))
|
||||
n_require_grad = sum(lmap(int, model_grads))
|
||||
npars = len(model_grads)
|
||||
assert not any(model_grads), f"{n_require_grad/npars:.1%} of {npars} weights require grad"
|
||||
|
||||
|
||||
def assert_not_all_frozen(model):
|
||||
model_grads: List[bool] = list(grad_status(model))
|
||||
npars = len(model_grads)
|
||||
assert any(model_grads), f"none of {npars} weights require grad"
|
||||
@@ -55,7 +55,7 @@ class ExamplesTests(unittest.TestCase):
|
||||
|
||||
testargs = """
|
||||
run_glue.py
|
||||
--model_name_or_path bert-base-uncased
|
||||
--model_name_or_path distilbert-base-uncased
|
||||
--data_dir ./tests/fixtures/tests_samples/MRPC/
|
||||
--task_name mrpc
|
||||
--do_train
|
||||
@@ -79,6 +79,7 @@ class ExamplesTests(unittest.TestCase):
|
||||
def test_run_language_modeling(self):
|
||||
stream_handler = logging.StreamHandler(sys.stdout)
|
||||
logger.addHandler(stream_handler)
|
||||
# TODO: switch to smaller model like sshleifer/tiny-distilroberta-base
|
||||
|
||||
testargs = """
|
||||
run_language_modeling.py
|
||||
@@ -105,10 +106,9 @@ class ExamplesTests(unittest.TestCase):
|
||||
|
||||
testargs = """
|
||||
run_squad.py
|
||||
--model_type=bert
|
||||
--model_name_or_path=bert-base-uncased
|
||||
--model_type=distilbert
|
||||
--model_name_or_path=sshleifer/tiny-distilbert-base-cased-distilled-squad
|
||||
--data_dir=./tests/fixtures/tests_samples/SQUAD
|
||||
--model_name=bert-base-uncased
|
||||
--output_dir=./tests/fixtures/tests_samples/temp_dir
|
||||
--max_steps=10
|
||||
--warmup_steps=2
|
||||
@@ -123,15 +123,15 @@ class ExamplesTests(unittest.TestCase):
|
||||
""".split()
|
||||
with patch.object(sys, "argv", testargs):
|
||||
result = run_squad.main()
|
||||
self.assertGreaterEqual(result["f1"], 30)
|
||||
self.assertGreaterEqual(result["exact"], 30)
|
||||
self.assertGreaterEqual(result["f1"], 25)
|
||||
self.assertGreaterEqual(result["exact"], 21)
|
||||
|
||||
def test_generation(self):
|
||||
stream_handler = logging.StreamHandler(sys.stdout)
|
||||
logger.addHandler(stream_handler)
|
||||
|
||||
testargs = ["run_generation.py", "--prompt=Hello", "--length=10", "--seed=42"]
|
||||
model_type, model_name = ("--model_type=openai-gpt", "--model_name_or_path=openai-gpt")
|
||||
model_type, model_name = ("--model_type=gpt2", "--model_name_or_path=sshleifer/tiny-gpt2")
|
||||
with patch.object(sys, "argv", testargs + [model_type, model_name]):
|
||||
result = run_generation.main()
|
||||
self.assertGreaterEqual(len(result[0]), 10)
|
||||
@@ -148,6 +148,9 @@ def perturb_past(
|
||||
for i in range(num_iterations):
|
||||
print("Iteration ", i + 1)
|
||||
curr_perturbation = [torch.from_numpy(p_).requires_grad_(True).to(device=device) for p_ in grad_accumulator]
|
||||
# make sure p_.grad is not None
|
||||
for p_ in curr_perturbation:
|
||||
p_.retain_grad()
|
||||
|
||||
# Compute hidden using perturbed past
|
||||
perturbed_past = list(map(add, past, curr_perturbation))
|
||||
|
||||
@@ -65,7 +65,7 @@ class NERTransformer(BaseTransformer):
|
||||
cls_token=self.tokenizer.cls_token,
|
||||
cls_token_segment_id=2 if self.config.model_type in ["xlnet"] else 0,
|
||||
sep_token=self.tokenizer.sep_token,
|
||||
sep_token_extra=bool(self.config.model_type in ["roberta"]),
|
||||
sep_token_extra=False,
|
||||
pad_on_left=bool(self.config.model_type in ["xlnet"]),
|
||||
pad_token=self.tokenizer.pad_token_id,
|
||||
pad_token_segment_id=self.tokenizer.pad_token_type_id,
|
||||
|
||||
@@ -119,7 +119,7 @@ if is_torch_available():
|
||||
cls_token=tokenizer.cls_token,
|
||||
cls_token_segment_id=2 if model_type in ["xlnet"] else 0,
|
||||
sep_token=tokenizer.sep_token,
|
||||
sep_token_extra=bool(model_type in ["roberta"]),
|
||||
sep_token_extra=False,
|
||||
# roberta uses an extra separator b/w pairs of sentences, cf. github.com/pytorch/fairseq/commit/1684e166e3da03f5b600dbb7855cb98ddfcd0805
|
||||
pad_on_left=bool(tokenizer.padding_side == "left"),
|
||||
pad_token=tokenizer.pad_token_id,
|
||||
@@ -172,7 +172,7 @@ if is_tf_available():
|
||||
cls_token=tokenizer.cls_token,
|
||||
cls_token_segment_id=2 if model_type in ["xlnet"] else 0,
|
||||
sep_token=tokenizer.sep_token,
|
||||
sep_token_extra=bool(model_type in ["roberta"]),
|
||||
sep_token_extra=False,
|
||||
# roberta uses an extra separator b/w pairs of sentences, cf. github.com/pytorch/fairseq/commit/1684e166e3da03f5b600dbb7855cb98ddfcd0805
|
||||
pad_on_left=bool(tokenizer.padding_side == "left"),
|
||||
pad_token=tokenizer.pad_token_id,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
thumbnail: https://raw.githubusercontent.com/JetRunner/BERT-of-Theseus/master/bert-of-theseus.png
|
||||
datasets:
|
||||
- multi_nli
|
||||
---
|
||||
|
||||
# BERT-of-Theseus
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
datasets:
|
||||
- squad_v2
|
||||
---
|
||||
|
||||
# roberta-base for QA
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
datasets:
|
||||
- squad
|
||||
---
|
||||
@@ -0,0 +1,3 @@
|
||||
GPT-2 (774M model) finetuned on 0.5m PubMed abstracts. Used in the [writemeanabstract.com](writemeanabstract.com) and the following preprint:
|
||||
|
||||
[Papanikolaou, Yannis, and Andrea Pierleoni. "DARE: Data Augmented Relation Extraction with GPT-2." arXiv preprint arXiv:2004.13845 (2020).](https://arxiv.org/abs/2004.13845)
|
||||
@@ -0,0 +1,3 @@
|
||||
GPT-2 (355M model) finetuned on 0.5m PubMed abstracts. Used in the [writemeanabstract.com](writemeanabstract.com) and the following preprint:
|
||||
|
||||
[Papanikolaou, Yannis, and Andrea Pierleoni. "DARE: Data Augmented Relation Extraction with GPT-2." arXiv preprint arXiv:2004.13845 (2020).](https://arxiv.org/abs/2004.13845)
|
||||
@@ -0,0 +1,3 @@
|
||||
## FinBERT
|
||||
|
||||
Code for importing and using this model is available [here](https://github.com/ipuneetrathore/BERT_models)
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
language: english
|
||||
thumbnail:
|
||||
datasets:
|
||||
- squad_v2
|
||||
---
|
||||
|
||||
# Longformer-base-4096 fine-tuned on SQuAD v2
|
||||
@@ -17,13 +18,19 @@ Longformer uses a combination of a sliding window (local) attention and global a
|
||||
|
||||
## Details of the downstream task (Q&A) - Dataset 📚 🧐 ❓
|
||||
|
||||
[SQuAD v2](https://rajpurkar.github.io/SQuAD-explorer/) combines the 100,000 questions in SQuAD1.1 with over 50,000 unanswerable questions written adversarially by crowdworkers to look similar to answerable ones. To do well on SQuAD2.0, systems must not only answer questions when possible, but also determine when no answer is supported by the paragraph and abstain from answering.
|
||||
|
||||
Dataset ID: ```squad_v2``` from [HugginFace/NLP](https://github.com/huggingface/nlp)
|
||||
| Dataset | Split | # samples |
|
||||
| -------- | ----- | --------- |
|
||||
| SQuAD2.0 | train | 130k |
|
||||
| SQuAD2.0 | eval | 12.3k |
|
||||
| squad_v2 | train | 130319 |
|
||||
| squad_v2 | valid | 11873 |
|
||||
|
||||
How to load it from [nlp](https://github.com/huggingface/nlp)
|
||||
|
||||
```python
|
||||
train_dataset = nlp.load_dataset('squad_v2', split=nlp.Split.TRAIN)
|
||||
valid_dataset = nlp.load_dataset('squad_v2', split=nlp.Split.VALIDATION)
|
||||
```
|
||||
Check out more about this dataset and others in [NLP Viewer](https://huggingface.co/nlp/viewer/)
|
||||
|
||||
|
||||
## Model fine-tuning 🏋️
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
language: english
|
||||
thumbnail:
|
||||
datasets:
|
||||
- squad_v2
|
||||
---
|
||||
|
||||
# T5-base fine-tuned on SQuAD v2
|
||||
@@ -16,13 +17,19 @@ Transfer learning, where a model is first pre-trained on a data-rich task before
|
||||
|
||||
## Details of the downstream task (Q&A) - Dataset 📚 🧐 ❓
|
||||
|
||||
[SQuAD v2](https://rajpurkar.github.io/SQuAD-explorer/) combines the 100,000 questions in SQuAD1.1 with over 50,000 unanswerable questions written adversarially by crowdworkers to look similar to answerable ones. To do well on SQuAD2.0, systems must not only answer questions when possible, but also determine when no answer is supported by the paragraph and abstain from answering.
|
||||
|
||||
Dataset ID: ```squad_v2``` from [HugginFace/NLP](https://github.com/huggingface/nlp)
|
||||
| Dataset | Split | # samples |
|
||||
| -------- | ----- | --------- |
|
||||
| SQuAD2.0 | train | 130k |
|
||||
| SQuAD2.0 | eval | 12.3k |
|
||||
| squad_v2 | train | 130319 |
|
||||
| squad_v2 | valid | 11873 |
|
||||
|
||||
How to load it from [nlp](https://github.com/huggingface/nlp)
|
||||
|
||||
```python
|
||||
train_dataset = nlp.load_dataset('squad_v2', split=nlp.Split.TRAIN)
|
||||
valid_dataset = nlp.load_dataset('squad_v2', split=nlp.Split.VALIDATION)
|
||||
```
|
||||
Check out more about this dataset and others in [NLP Viewer](https://huggingface.co/nlp/viewer/)
|
||||
|
||||
|
||||
## Model fine-tuning 🏋️
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
language: english
|
||||
thumbnail:
|
||||
---
|
||||
|
||||
# T5-base fine-tuned fo News Summarization 📖✏️🧾
|
||||
|
||||
All credits to [Abhishek Kumar Mishra](https://github.com/abhimishra91)
|
||||
|
||||
[Google's T5](https://ai.googleblog.com/2020/02/exploring-transfer-learning-with-t5.html) base fine-tuned on [News Summary](https://www.kaggle.com/sunnysai12345/news-summary) dataset for **summarization** downstream task.
|
||||
|
||||
## Details of T5
|
||||
|
||||
The **T5** model was presented in [Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer](https://arxiv.org/pdf/1910.10683.pdf) by *Colin Raffel, Noam Shazeer, Adam Roberts, Katherine Lee, Sharan Narang, Michael Matena, Yanqi Zhou, Wei Li, Peter J. Liu* in Here the abstract:
|
||||
|
||||
Transfer learning, where a model is first pre-trained on a data-rich task before being fine-tuned on a downstream task, has emerged as a powerful technique in natural language processing (NLP). The effectiveness of transfer learning has given rise to a diversity of approaches, methodology, and practice. In this paper, we explore the landscape of transfer learning techniques for NLP by introducing a unified framework that converts every language problem into a text-to-text format. Our systematic study compares pre-training objectives, architectures, unlabeled datasets, transfer approaches, and other factors on dozens of language understanding tasks. By combining the insights from our exploration with scale and our new “Colossal Clean Crawled Corpus”, we achieve state-of-the-art results on many benchmarks covering summarization, question answering, text classification, and more. To facilitate future work on transfer learning for NLP, we release our dataset, pre-trained models, and code.
|
||||
|
||||

|
||||
|
||||
## Details of the downstream task (Summarization) - Dataset 📚
|
||||
|
||||
[News Summary](https://www.kaggle.com/sunnysai12345/news-summary)
|
||||
|
||||
The dataset consists of **4515 examples** and contains Author_name, Headlines, Url of Article, Short text, Complete Article. I gathered the summarized news from Inshorts and only scraped the news articles from Hindu, Indian times and Guardian. Time period ranges from febrauary to august 2017.
|
||||
|
||||
|
||||
|
||||
|
||||
## Model fine-tuning 🏋️
|
||||
|
||||
The training script is a slightly modified version of [this Colab Notebook](https://github.com/abhimishra91/transformers-tutorials/blob/master/transformers_summarization_wandb.ipynb) created by [Abhishek Kumar Mishra](https://github.com/abhimishra91), so all credits to him!
|
||||
I also trained the model for more epochs (6).
|
||||
|
||||
|
||||
## Model in Action 🚀
|
||||
|
||||
```python
|
||||
from transformers import AutoModelWithLMHead, AutoTokenizer
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained("mrm8488/t5-base-finetuned-summarize-news")
|
||||
model = AutoModelWithLMHead.from_pretrained("mrm8488/t5-base-finetuned-summarize-news")
|
||||
|
||||
def summarize(text, max_length=150):
|
||||
input_ids = tokenizer.encode(text, return_tensors="pt", add_special_tokens=True)
|
||||
|
||||
generated_ids = model.generate(input_ids=input_ids, num_beams=2, max_length=max_length, repetition_penalty=2.5, length_penalty=1.0, early_stopping=True)
|
||||
|
||||
preds = [tokenizer.decode(g, skip_special_tokens=True, clean_up_tokenization_spaces=True) for g in generated_ids]
|
||||
|
||||
return preds[0]
|
||||
```
|
||||
Given the following article from **NYT** (2020/06/09) with title *George Floyd’s death energized a movement. He will be buried in Houston today*:
|
||||
|
||||
After the sound and the fury, weeks of demonstrations and anguished calls for racial justice, the man whose death gave rise to an international movement, and whose last words — “I can’t breathe” — have been a rallying cry, will be laid to rest on Tuesday at a private funeral in Houston.George Floyd, who was 46, will then be buried in a grave next to his mother’s.The service, scheduled to begin at 11 a.m. at the Fountain of Praise church, comes after five days of public memorials in Minneapolis, North Carolina and Houston and two weeks after a Minneapolis police officer was caught on video pressing his knee into Mr. Floyd’s neck for nearly nine minutes before Mr. Floyd died. That officer, Derek Chauvin, has been charged with second-degree murder and second-degree manslaughter. His bail was set at $1.25 million in a court appearance on Monday. The outpouring of anger and outrage after Mr. Floyd’s death — and the speed at which protests spread from tense, chaotic demonstrations in the city where he died to an international movement from Rome to Rio de Janeiro — has reflected the depth of frustration borne of years of watching black people die at the hands of the police or vigilantes while calls for change went unmet.
|
||||
```
|
||||
summarize('After the sound and the fury, weeks of demonstrations and anguished calls for racial justice, the man whose death gave rise to an international movement, and whose last words — “I can’t breathe” — have been a rallying cry, will be laid to rest on Tuesday at a private funeral in Houston.George Floyd, who was 46, will then be buried in a grave next to his mother’s.The service, scheduled to begin at 11 a.m. at the Fountain of Praise church, comes after five days of public memorials in Minneapolis, North Carolina and Houston and two weeks after a Minneapolis police officer was caught on video pressing his knee into Mr. Floyd’s neck for nearly nine minutes before Mr. Floyd died. That officer, Derek Chauvin, has been charged with second-degree murder and second-degree manslaughter. His bail was set at $1.25 million in a court appearance on Monday. The outpouring of anger and outrage after Mr. Floyd’s death — and the speed at which protests spread from tense, chaotic demonstrations in the city where he died to an international movement from Rome to Rio de Janeiro — has reflected the depth of frustration borne of years of watching black people die at the hands of the police or vigilantes while calls for change went unmet.', 80)
|
||||
```
|
||||
We would obtain:
|
||||
|
||||
At a private funeral in Houston. Floyd, who was 46 years old when his death occurred, will be buried next to the grave of his mother. A Minnesota police officer was caught on video pressing his knee into Mr's neck for nearly nine minutes before his death. The officer has been charged with second-degree manslaughter and $1.2 million bail is set at
|
||||
|
||||
> Created by [Manuel Romero/@mrm8488](https://twitter.com/mrm8488) | [LinkedIn](https://www.linkedin.com/in/manuel-romero-cs/)
|
||||
|
||||
> Made with <span style="color: #e25555;">♥</span> in Spain
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
datasets:
|
||||
- squad
|
||||
---
|
||||
|
||||
# BART-LARGE finetuned on SQuADv1
|
||||
|
||||
This is bart-large model finetuned on SQuADv1 dataset for question answering task
|
||||
|
||||
## Model details
|
||||
BART was propsed in the [paper](https://arxiv.org/abs/1910.13461) **BART: Denoising Sequence-to-Sequence Pre-training for Natural Language Generation, Translation, and Comprehension**.
|
||||
BART is a seq2seq model intended for both NLG and NLU tasks.
|
||||
|
||||
To use BART for question answering tasks, we feed the complete document into the encoder and decoder, and use the top
|
||||
hidden state of the decoder as a representation for each
|
||||
word. This representation is used to classify the token. As given in the paper bart-large achives comparable to ROBERTa on SQuAD.
|
||||
Another notable thing about BART is that it can handle sequences with upto 1024 tokens.
|
||||
|
||||
| Param | #Value |
|
||||
|---------------------|--------|
|
||||
| encoder layers | 12 |
|
||||
| decoder layers | 12 |
|
||||
| hidden size | 4096 |
|
||||
| num attetion heads | 16 |
|
||||
| on disk size | 1.63GB |
|
||||
|
||||
|
||||
## Model training
|
||||
This model was trained on google colab v100 GPU.
|
||||
You can find the fine-tuning colab here
|
||||
[](https://colab.research.google.com/drive/1I5cK1M_0dLaf5xoewh6swcm5nAInfwHy?usp=sharing).
|
||||
|
||||
|
||||
## Results
|
||||
The results are actually slightly worse than given in the paper.
|
||||
In the paper the authors mentioned that bart-large achieves 88.8 EM and 94.6 F1
|
||||
|
||||
| Metric | #Value |
|
||||
|--------|--------|
|
||||
| EM | 86.8022|
|
||||
| F1 | 92.7342|
|
||||
|
||||
|
||||
## Model in Action 🚀
|
||||
```python3
|
||||
from transformers import BartTokenizer, BartForQuestionAnswering
|
||||
import torch
|
||||
|
||||
tokenizer = BartTokenizer.from_pretrained('valhalla/bart-large-finetuned-squadv1')
|
||||
model = BartForQuestionAnswering.from_pretrained('valhalla/bart-large-finetuned-squadv1')
|
||||
|
||||
question, text = "Who was Jim Henson?", "Jim Henson was a nice puppet"
|
||||
encoding = tokenizer.encode_plus(question, text, return_tensors='pt')
|
||||
input_ids = encoding['input_ids']
|
||||
attention_mask = encoding['attention_mask']
|
||||
|
||||
start_scores, end_scores = model(input_ids, attention_mask=attention_mask, output_attentions=False)[:2]
|
||||
|
||||
all_tokens = tokenizer.convert_ids_to_tokens(input_ids[0])
|
||||
answer = ' '.join(all_tokens[torch.argmax(start_scores) : torch.argmax(end_scores)+1])
|
||||
answer = tokenizer.convert_tokens_to_ids(answer.split())
|
||||
answer = tokenizer.decode(answer)
|
||||
#answer => 'a nice puppet'
|
||||
```
|
||||
|
||||
> Created with ❤️ by Suraj Patil [](https://github.com/patil-suraj/)
|
||||
[](https://twitter.com/psuraj28)
|
||||
@@ -0,0 +1,47 @@
|
||||
# ELECTRA-BASE-DISCRIMINATOR finetuned on SQuADv1
|
||||
|
||||
This is electra-base-discriminator model finetuned on SQuADv1 dataset for for question answering task.
|
||||
|
||||
## Model details
|
||||
As mentioned in the original paper: ELECTRA is a new method for self-supervised language representation learning.
|
||||
It can be used to pre-train transformer networks using relatively little compute.
|
||||
ELECTRA models are trained to distinguish "real" input tokens vs "fake" input tokens generated by another neural network,
|
||||
similar to the discriminator of a GAN. At small scale, ELECTRA achieves strong results even when trained on a single GPU.
|
||||
At large scale, ELECTRA achieves state-of-the-art results on the SQuAD 2.0 dataset.
|
||||
|
||||
| Param | #Value |
|
||||
|---------------------|--------|
|
||||
| layers | 12 |
|
||||
| hidden size | 768 |
|
||||
| num attetion heads | 12 |
|
||||
| on disk size | 436MB |
|
||||
|
||||
## Model training
|
||||
This model was trained on google colab v100 GPU.
|
||||
You can find the fine-tuning colab here
|
||||
[](https://colab.research.google.com/drive/11yo-LaFsgggwmDSy2P8zD3tzf5cCb-DU?usp=sharing).
|
||||
|
||||
## Results
|
||||
The results are actually slightly better than given in the paper.
|
||||
In the paper the authors mentioned that electra-base achieves 84.5 EM and 90.8 F1
|
||||
|
||||
| Metric | #Value |
|
||||
|--------|--------|
|
||||
| EM | 85.0520|
|
||||
| F1 | 91.6050|
|
||||
|
||||
|
||||
## Model in Action 🚀
|
||||
```python3
|
||||
from transformers import pipeline
|
||||
|
||||
nlp = pipeline('question-answering', model='valhalla/electra-base-discriminator-finetuned_squadv1')
|
||||
nlp({
|
||||
'question': 'What is the answer to everything ?',
|
||||
'context': '42 is the answer to life the universe and everything'
|
||||
})
|
||||
=> {'answer': '42', 'end': 2, 'score': 0.981274963050339, 'start': 0}
|
||||
```
|
||||
|
||||
> Created with ❤️ by Suraj Patil [](https://github.com/patil-suraj/)
|
||||
[](https://twitter.com/psuraj28)
|
||||
@@ -5,15 +5,19 @@ include_trailing_comma = True
|
||||
known_first_party = transformers
|
||||
known_third_party =
|
||||
absl
|
||||
elasticsearch
|
||||
fairseq
|
||||
faiss
|
||||
fastprogress
|
||||
git
|
||||
h5py
|
||||
matplotlib
|
||||
MeCab
|
||||
nlp
|
||||
nltk
|
||||
numpy
|
||||
packaging
|
||||
pandas
|
||||
PIL
|
||||
psutil
|
||||
pytorch_lightning
|
||||
|
||||
@@ -108,7 +108,7 @@ setup(
|
||||
packages=find_packages("src"),
|
||||
install_requires=[
|
||||
"numpy",
|
||||
"tokenizers == 0.7.0",
|
||||
"tokenizers == 0.8.0-rc1",
|
||||
# dataclasses for Python versions that don't have it
|
||||
"dataclasses;python_version<'3.7'",
|
||||
# utilities from PyPA to e.g. compare versions
|
||||
|
||||
@@ -36,6 +36,7 @@ from .configuration_marian import MarianConfig
|
||||
from .configuration_mmbt import MMBTConfig
|
||||
from .configuration_openai import OPENAI_GPT_PRETRAINED_CONFIG_ARCHIVE_MAP, OpenAIGPTConfig
|
||||
from .configuration_reformer import REFORMER_PRETRAINED_CONFIG_ARCHIVE_MAP, ReformerConfig
|
||||
from .configuration_retribert import RETRIBERT_PRETRAINED_CONFIG_ARCHIVE_MAP, RetriBertConfig
|
||||
from .configuration_roberta import ROBERTA_PRETRAINED_CONFIG_ARCHIVE_MAP, RobertaConfig
|
||||
from .configuration_t5 import T5_PRETRAINED_CONFIG_ARCHIVE_MAP, T5Config
|
||||
from .configuration_transfo_xl import TRANSFO_XL_PRETRAINED_CONFIG_ARCHIVE_MAP, TransfoXLConfig
|
||||
@@ -76,6 +77,9 @@ from .file_utils import (
|
||||
add_end_docstrings,
|
||||
add_start_docstrings,
|
||||
cached_path,
|
||||
is_apex_available,
|
||||
is_psutil_available,
|
||||
is_py3nvml_available,
|
||||
is_tf_available,
|
||||
is_torch_available,
|
||||
is_torch_tpu_available,
|
||||
@@ -118,7 +122,7 @@ from .pipelines import (
|
||||
# Tokenizers
|
||||
from .tokenization_albert import AlbertTokenizer
|
||||
from .tokenization_auto import TOKENIZER_MAPPING, AutoTokenizer
|
||||
from .tokenization_bart import BartTokenizer, MBartTokenizer
|
||||
from .tokenization_bart import BartTokenizer, BartTokenizerFast, MBartTokenizer
|
||||
from .tokenization_bert import BasicTokenizer, BertTokenizer, BertTokenizerFast, WordpieceTokenizer
|
||||
from .tokenization_bert_japanese import BertJapaneseTokenizer, CharacterTokenizer, MecabTokenizer
|
||||
from .tokenization_camembert import CamembertTokenizer
|
||||
@@ -130,16 +134,20 @@ from .tokenization_gpt2 import GPT2Tokenizer, GPT2TokenizerFast
|
||||
from .tokenization_longformer import LongformerTokenizer, LongformerTokenizerFast
|
||||
from .tokenization_openai import OpenAIGPTTokenizer, OpenAIGPTTokenizerFast
|
||||
from .tokenization_reformer import ReformerTokenizer
|
||||
from .tokenization_retribert import RetriBertTokenizer, RetriBertTokenizerFast
|
||||
from .tokenization_roberta import RobertaTokenizer, RobertaTokenizerFast
|
||||
from .tokenization_t5 import T5Tokenizer
|
||||
from .tokenization_transfo_xl import TransfoXLCorpus, TransfoXLTokenizer, TransfoXLTokenizerFast
|
||||
from .tokenization_utils import (
|
||||
from .tokenization_utils import PreTrainedTokenizer
|
||||
from .tokenization_utils_base import (
|
||||
BatchEncoding,
|
||||
PreTrainedTokenizer,
|
||||
PreTrainedTokenizerFast,
|
||||
CharSpan,
|
||||
PreTrainedTokenizerBase,
|
||||
SpecialTokensMixin,
|
||||
TensorType,
|
||||
TokenSpan,
|
||||
)
|
||||
from .tokenization_utils_fast import PreTrainedTokenizerFast
|
||||
from .tokenization_xlm import XLMTokenizer
|
||||
from .tokenization_xlm_roberta import XLMRobertaTokenizer
|
||||
from .tokenization_xlnet import SPIECE_UNDERLINE, XLNetTokenizer
|
||||
@@ -166,11 +174,17 @@ if is_torch_available():
|
||||
AutoModelForSequenceClassification,
|
||||
AutoModelForQuestionAnswering,
|
||||
AutoModelWithLMHead,
|
||||
AutoModelForCausalLM,
|
||||
AutoModelForMaskedLM,
|
||||
AutoModelForSeq2SeqLM,
|
||||
AutoModelForTokenClassification,
|
||||
AutoModelForMultipleChoice,
|
||||
MODEL_MAPPING,
|
||||
MODEL_FOR_PRETRAINING_MAPPING,
|
||||
MODEL_WITH_LM_HEAD_MAPPING,
|
||||
MODEL_FOR_CAUSAL_LM_MAPPING,
|
||||
MODEL_FOR_MASKED_LM_MAPPING,
|
||||
MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING,
|
||||
MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING,
|
||||
MODEL_FOR_QUESTION_ANSWERING_MAPPING,
|
||||
MODEL_FOR_TOKEN_CLASSIFICATION_MAPPING,
|
||||
@@ -182,6 +196,7 @@ if is_torch_available():
|
||||
BertModel,
|
||||
BertForPreTraining,
|
||||
BertForMaskedLM,
|
||||
BertLMHeadModel,
|
||||
BertForNextSentencePrediction,
|
||||
BertForSequenceClassification,
|
||||
BertForMultipleChoice,
|
||||
@@ -243,6 +258,7 @@ if is_torch_available():
|
||||
BartForSequenceClassification,
|
||||
BartModel,
|
||||
BartForConditionalGeneration,
|
||||
BartForQuestionAnswering,
|
||||
BART_PRETRAINED_MODEL_ARCHIVE_LIST,
|
||||
)
|
||||
from .modeling_marian import MarianMTModel
|
||||
@@ -260,6 +276,7 @@ if is_torch_available():
|
||||
DistilBertPreTrainedModel,
|
||||
DistilBertForMaskedLM,
|
||||
DistilBertModel,
|
||||
DistilBertForMultipleChoice,
|
||||
DistilBertForSequenceClassification,
|
||||
DistilBertForQuestionAnswering,
|
||||
DistilBertForTokenClassification,
|
||||
@@ -287,6 +304,7 @@ if is_torch_available():
|
||||
AlbertModel,
|
||||
AlbertForPreTraining,
|
||||
AlbertForMaskedLM,
|
||||
AlbertForMultipleChoice,
|
||||
AlbertForSequenceClassification,
|
||||
AlbertForQuestionAnswering,
|
||||
AlbertForTokenClassification,
|
||||
@@ -319,6 +337,7 @@ if is_torch_available():
|
||||
ElectraForTokenClassification,
|
||||
ElectraPreTrainedModel,
|
||||
ElectraForSequenceClassification,
|
||||
ElectraForQuestionAnswering,
|
||||
ElectraModel,
|
||||
load_tf_weights_in_electra,
|
||||
ELECTRA_PRETRAINED_MODEL_ARCHIVE_LIST,
|
||||
@@ -342,6 +361,12 @@ if is_torch_available():
|
||||
LONGFORMER_PRETRAINED_MODEL_ARCHIVE_LIST,
|
||||
)
|
||||
|
||||
from .modeling_retribert import (
|
||||
RetriBertPreTrainedModel,
|
||||
RetriBertModel,
|
||||
RETRIBERT_PRETRAINED_MODEL_ARCHIVE_LIST,
|
||||
)
|
||||
|
||||
# Optimization
|
||||
from .optimization import (
|
||||
AdamW,
|
||||
@@ -354,11 +379,12 @@ if is_torch_available():
|
||||
|
||||
# Trainer
|
||||
from .trainer import Trainer, set_seed, torch_distributed_zero_first, EvalPrediction
|
||||
from .data.data_collator import DefaultDataCollator, DataCollator, DataCollatorForLanguageModeling
|
||||
from .data.data_collator import default_data_collator, DataCollator, DataCollatorForLanguageModeling
|
||||
from .data.datasets import GlueDataset, TextDataset, LineByLineTextDataset, GlueDataTrainingArguments
|
||||
|
||||
# Benchmarks
|
||||
from .benchmark import PyTorchBenchmark, PyTorchBenchmarkArguments
|
||||
from .benchmark.benchmark import PyTorchBenchmark
|
||||
from .benchmark.benchmark_args import PyTorchBenchmarkArguments
|
||||
|
||||
# TensorFlow
|
||||
if is_tf_available():
|
||||
@@ -554,6 +580,10 @@ if is_tf_available():
|
||||
# Trainer
|
||||
from .trainer_tf import TFTrainer
|
||||
|
||||
# Benchmarks
|
||||
from .benchmark.benchmark_tf import TensorflowBenchmark
|
||||
from .benchmark.benchmark_args_tf import TensorflowBenchmarkArguments
|
||||
|
||||
|
||||
if not is_tf_available() and not is_torch_available():
|
||||
logger.warning(
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# flake8: noqa
|
||||
# There's no way to ignore "F401 '...' imported but unused" warnings in this
|
||||
# module, but to preserve other warnings. So, don't check this module at all.
|
||||
|
||||
from ..file_utils import is_torch_available
|
||||
|
||||
|
||||
if is_torch_available():
|
||||
from .benchmark_args import PyTorchBenchmarkArguments
|
||||
from .benchmark import PyTorchBenchmark
|
||||
@@ -25,8 +25,8 @@ from transformers import (
|
||||
MODEL_MAPPING,
|
||||
MODEL_WITH_LM_HEAD_MAPPING,
|
||||
PretrainedConfig,
|
||||
is_py3nvml_available,
|
||||
is_torch_available,
|
||||
is_torch_tpu_available,
|
||||
)
|
||||
|
||||
from .benchmark_utils import Benchmark, Memory, measure_peak_memory_cpu, start_memory_tracing, stop_memory_tracing
|
||||
@@ -37,6 +37,10 @@ if is_torch_available():
|
||||
from .benchmark_args import PyTorchBenchmarkArguments
|
||||
|
||||
|
||||
if is_py3nvml_available():
|
||||
import py3nvml.py3nvml as nvml
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -50,221 +54,169 @@ class PyTorchBenchmark(Benchmark):
|
||||
def framework_version(self):
|
||||
return torch.__version__
|
||||
|
||||
def train(self, model_name, batch_size, sequence_length, trace_memory=False):
|
||||
try:
|
||||
config = self.config_dict[model_name]
|
||||
def _inference_speed(self, model_name, batch_size, sequence_length):
|
||||
_inference = self._prepare_inference_func(model_name, batch_size, sequence_length)
|
||||
return self._measure_speed(_inference)
|
||||
|
||||
if self.args.torchscript:
|
||||
config.torchscript = True
|
||||
def _inference_memory(self, model_name, batch_size, sequence_length):
|
||||
_inference = self._prepare_inference_func(model_name, batch_size, sequence_length)
|
||||
return self._measure_memory(_inference)
|
||||
|
||||
def _train_speed(self, model_name, batch_size, sequence_length):
|
||||
_train = self._prepare_train_func(model_name, batch_size, sequence_length)
|
||||
return self._measure_speed(_train)
|
||||
|
||||
def _train_memory(self, model_name, batch_size, sequence_length):
|
||||
_train = self._prepare_train_func(model_name, batch_size, sequence_length)
|
||||
return self._measure_memory(_train)
|
||||
|
||||
def _prepare_inference_func(self, model_name, batch_size, sequence_length):
|
||||
config = self.config_dict[model_name]
|
||||
|
||||
if self.args.torchscript:
|
||||
config.torchscript = True
|
||||
if self.args.with_lm_head:
|
||||
model = MODEL_WITH_LM_HEAD_MAPPING[config.__class__](config)
|
||||
model.to(self.args.device)
|
||||
model.train()
|
||||
else:
|
||||
model = MODEL_MAPPING[config.__class__](config)
|
||||
|
||||
# encoder-decoder has vocab size saved differently
|
||||
vocab_size = config.vocab_size if hasattr(config, "vocab_size") else config.encoder.vocab_size
|
||||
input_ids = torch.randint(
|
||||
vocab_size, (batch_size, sequence_length), dtype=torch.long, device=self.args.device
|
||||
)
|
||||
model.eval()
|
||||
model.to(self.args.device)
|
||||
|
||||
if self.args.torchscript:
|
||||
raise NotImplementedError("Training for torchscript is currently not implemented")
|
||||
else:
|
||||
train_model = model
|
||||
# encoder-decoder has vocab size saved differently
|
||||
vocab_size = config.vocab_size if hasattr(config, "vocab_size") else config.encoder.vocab_size
|
||||
input_ids = torch.randint(vocab_size, (batch_size, sequence_length), dtype=torch.long, device=self.args.device)
|
||||
|
||||
def compute_loss_and_backprob_encoder():
|
||||
loss = train_model(input_ids, labels=input_ids)[0]
|
||||
loss.backward()
|
||||
train_model.zero_grad()
|
||||
if self.args.fp16:
|
||||
logger.info("Running training in Mixed Precision...")
|
||||
assert self.args.is_gpu, "Mixed precision is possible only for GPU."
|
||||
# amp seems to have memory leaks so that memory usage
|
||||
# is measured using .half() for now https://github.com/NVIDIA/apex/issues/439
|
||||
model.half()
|
||||
|
||||
def compute_loss_and_backprob_encoder_decoder():
|
||||
loss = train_model(input_ids, decoder_input_ids=input_ids, labels=input_ids)[0]
|
||||
loss.backward()
|
||||
train_model.zero_grad()
|
||||
if self.args.torchscript:
|
||||
with torch.no_grad():
|
||||
inference_model = torch.jit.trace(model, input_ids)
|
||||
else:
|
||||
inference_model = model
|
||||
|
||||
_train = (
|
||||
compute_loss_and_backprob_encoder_decoder
|
||||
if config.is_encoder_decoder
|
||||
else compute_loss_and_backprob_encoder
|
||||
)
|
||||
def encoder_decoder_forward():
|
||||
with torch.no_grad():
|
||||
outputs = inference_model(input_ids, decoder_input_ids=input_ids)
|
||||
return outputs
|
||||
|
||||
if trace_memory is True:
|
||||
if self.args.trace_memory_line_by_line:
|
||||
trace = start_memory_tracing("transformers")
|
||||
def encoder_forward():
|
||||
with torch.no_grad():
|
||||
outputs = inference_model(input_ids)
|
||||
return outputs
|
||||
|
||||
if self.args.n_gpu > 0:
|
||||
# gpu
|
||||
# clear gpu cache
|
||||
torch.cuda.empty_cache()
|
||||
if hasattr(torch.cuda, "max_memory_reserved"):
|
||||
torch.cuda.reset_peak_memory_stats()
|
||||
else:
|
||||
logger.info(
|
||||
"Please consider updating PyTorch to version 1.4 to get more accuracy on GPU memory usage"
|
||||
)
|
||||
torch.cuda.reset_max_memory_cached()
|
||||
_forward = encoder_decoder_forward if config.is_encoder_decoder else encoder_forward
|
||||
return _forward
|
||||
|
||||
# calculate loss and do backpropagation
|
||||
_train()
|
||||
elif not self.args.no_tpu and is_torch_tpu_available():
|
||||
# tpu
|
||||
raise NotImplementedError(
|
||||
"Memory Benchmarking is currently not implemented for TPU. Please disable memory benchmarking with `args.no_memory=True`"
|
||||
)
|
||||
else:
|
||||
# cpu
|
||||
memory_bytes = measure_peak_memory_cpu(_train)
|
||||
memory = Memory(memory_bytes) if isinstance(memory_bytes, int) else memory_bytes
|
||||
def _prepare_train_func(self, model_name, batch_size, sequence_length):
|
||||
config = self.config_dict[model_name]
|
||||
model = MODEL_WITH_LM_HEAD_MAPPING[config.__class__](config)
|
||||
|
||||
if self.args.trace_memory_line_by_line:
|
||||
summary = stop_memory_tracing(trace)
|
||||
else:
|
||||
summary = None
|
||||
if self.args.torchscript:
|
||||
raise NotImplementedError("Training for torchscript is currently not implemented")
|
||||
else:
|
||||
train_model = model
|
||||
|
||||
if self.args.n_gpu > 0:
|
||||
# gpu
|
||||
if hasattr(torch.cuda, "max_memory_reserved"):
|
||||
memory = Memory(torch.cuda.max_memory_reserved())
|
||||
else:
|
||||
logger.info(
|
||||
"Please consider updating PyTorch to version 1.4 to get more accuracy on GPU memory usage"
|
||||
)
|
||||
memory = Memory(torch.cuda.max_memory_cached())
|
||||
memory = Memory(torch.cuda.max_memory_reserved())
|
||||
model.eval()
|
||||
model.to(self.args.device)
|
||||
|
||||
return memory, summary
|
||||
else:
|
||||
if (not self.args.no_tpu and is_torch_tpu_available()) or self.args.torchscript:
|
||||
# run additional 10 times to stabilize compilation for tpu and torchscript
|
||||
logger.info("Do inference on TPU or torchscript. Running model 5 times to stabilize compilation")
|
||||
timeit.repeat(
|
||||
_train, repeat=1, number=5,
|
||||
)
|
||||
# encoder-decoder has vocab size saved differently
|
||||
vocab_size = config.vocab_size if hasattr(config, "vocab_size") else config.encoder.vocab_size
|
||||
input_ids = torch.randint(vocab_size, (batch_size, sequence_length), dtype=torch.long, device=self.args.device)
|
||||
|
||||
# as written in https://docs.python.org/2/library/timeit.html#timeit.Timer.repeat, min should be taken rather than the average
|
||||
runtimes = timeit.repeat(_train, repeat=self.args.repeat, number=10,)
|
||||
if self.args.fp16:
|
||||
logger.info("Running training in Mixed Precision...")
|
||||
assert self.args.is_gpu, "Mixed precision is possible only for GPU."
|
||||
|
||||
if not self.args.no_tpu and is_torch_tpu_available() and self.args.tpu_print_metrics:
|
||||
import torch_xla.debug.metrics as met
|
||||
# amp seems to have memory leaks so that memory usage
|
||||
# is measured using .half() for now https://github.com/NVIDIA/apex/issues/439
|
||||
model.half()
|
||||
|
||||
self.print_fn(met.metrics_report())
|
||||
def compute_loss_and_backprob_encoder():
|
||||
loss = train_model(input_ids, labels=input_ids)[0]
|
||||
loss.backward()
|
||||
train_model.zero_grad()
|
||||
|
||||
return min(runtimes) / 10.0
|
||||
except RuntimeError as e:
|
||||
self.print_fn("Doesn't fit on GPU. {}".format(e))
|
||||
if trace_memory:
|
||||
return "N/A", None
|
||||
else:
|
||||
return "N/A"
|
||||
def compute_loss_and_backprob_encoder_decoder():
|
||||
loss = train_model(input_ids, decoder_input_ids=input_ids, labels=input_ids)[0]
|
||||
loss.backward()
|
||||
train_model.zero_grad()
|
||||
|
||||
def inference(self, model_name, batch_size, sequence_length, trace_memory=False):
|
||||
_train = (
|
||||
compute_loss_and_backprob_encoder_decoder
|
||||
if config.is_encoder_decoder
|
||||
else compute_loss_and_backprob_encoder
|
||||
)
|
||||
return _train
|
||||
|
||||
def _measure_speed(self, func):
|
||||
try:
|
||||
config = self.config_dict[model_name]
|
||||
model = None
|
||||
if self.args.is_tpu or self.args.torchscript:
|
||||
# run additional 10 times to stabilize compilation for tpu and torchscript
|
||||
logger.info("Do inference on TPU or torchscript. Running model 5 times to stabilize compilation")
|
||||
timeit.repeat(
|
||||
func, repeat=1, number=5,
|
||||
)
|
||||
|
||||
if self.args.torchscript:
|
||||
config.torchscript = True
|
||||
# as written in https://docs.python.org/2/library/timeit.html#timeit.Timer.repeat, min should be taken rather than the average
|
||||
runtimes = timeit.repeat(func, repeat=self.args.repeat, number=10,)
|
||||
|
||||
if self.args.with_lm_head:
|
||||
model = MODEL_WITH_LM_HEAD_MAPPING[config.__class__](config)
|
||||
else:
|
||||
model = MODEL_MAPPING[config.__class__](config)
|
||||
if self.args.is_tpu and self.args.torch_xla_tpu_print_metrics:
|
||||
import torch_xla.debug.metrics as met
|
||||
|
||||
model.eval()
|
||||
model.to(self.args.device)
|
||||
|
||||
# encoder-decoder has vocab size saved differently
|
||||
vocab_size = config.vocab_size if hasattr(config, "vocab_size") else config.encoder.vocab_size
|
||||
|
||||
input_ids = torch.randint(
|
||||
vocab_size, (batch_size, sequence_length), dtype=torch.long, device=self.args.device
|
||||
)
|
||||
|
||||
if self.args.torchscript:
|
||||
with torch.no_grad():
|
||||
if config.is_encoder_decoder:
|
||||
raise NotImplementedError("Torchscript is currently not supported for EncoderDecoder models")
|
||||
else:
|
||||
inference_model = torch.jit.trace(model, input_ids)
|
||||
else:
|
||||
inference_model = model
|
||||
|
||||
def encoder_decoder_forward():
|
||||
with torch.no_grad():
|
||||
inference_model(input_ids, decoder_input_ids=input_ids)
|
||||
|
||||
def encoder_forward():
|
||||
with torch.no_grad():
|
||||
inference_model(input_ids)
|
||||
|
||||
_forward = encoder_decoder_forward if config.is_encoder_decoder else encoder_forward
|
||||
|
||||
if trace_memory is True:
|
||||
if self.args.trace_memory_line_by_line:
|
||||
trace = start_memory_tracing("transformers")
|
||||
|
||||
if self.args.n_gpu > 0:
|
||||
# gpu
|
||||
# clear gpu cache
|
||||
torch.cuda.empty_cache()
|
||||
if hasattr(torch.cuda, "max_memory_reserved"):
|
||||
torch.cuda.reset_peak_memory_stats()
|
||||
else:
|
||||
logger.info(
|
||||
"Please consider updating PyTorch to version 1.4 to get more accuracy on GPU memory usage"
|
||||
)
|
||||
torch.cuda.reset_max_memory_cached()
|
||||
|
||||
# run forward
|
||||
_forward()
|
||||
elif not self.args.no_tpu and is_torch_tpu_available():
|
||||
# tpu
|
||||
raise NotImplementedError(
|
||||
"Memory Benchmarking is currently not implemented for TPU. Please disable memory benchmarking with `args.no_memory=True`"
|
||||
)
|
||||
else:
|
||||
# cpu
|
||||
memory_bytes = measure_peak_memory_cpu(_forward)
|
||||
memory = Memory(memory_bytes) if isinstance(memory_bytes, int) else memory_bytes
|
||||
|
||||
if self.args.trace_memory_line_by_line:
|
||||
summary = stop_memory_tracing(trace)
|
||||
else:
|
||||
summary = None
|
||||
|
||||
if self.args.n_gpu > 0:
|
||||
# gpu
|
||||
if hasattr(torch.cuda, "max_memory_reserved"):
|
||||
memory = Memory(torch.cuda.max_memory_reserved())
|
||||
else:
|
||||
logger.info(
|
||||
"Please consider updating PyTorch to version 1.4 to get more accuracy on GPU memory usage"
|
||||
)
|
||||
memory = Memory(torch.cuda.max_memory_cached())
|
||||
|
||||
return memory, summary
|
||||
else:
|
||||
|
||||
if (not self.args.no_tpu and is_torch_tpu_available()) or self.args.torchscript:
|
||||
# run additional 10 times to stabilize compilation for tpu and torchscript
|
||||
logger.info("Do inference on TPU or torchscript. Running model 5 times to stabilize compilation")
|
||||
timeit.repeat(
|
||||
_forward, repeat=1, number=5,
|
||||
)
|
||||
|
||||
# as written in https://docs.python.org/2/library/timeit.html#timeit.Timer.repeat, min should be taken rather than the average
|
||||
runtimes = timeit.repeat(_forward, repeat=self.args.repeat, number=10,)
|
||||
|
||||
if not self.args.no_tpu and is_torch_tpu_available() and self.args.tpu_print_metrics:
|
||||
import torch_xla.debug.metrics as met
|
||||
|
||||
self.print_fn(met.metrics_report())
|
||||
|
||||
return min(runtimes) / 10.0
|
||||
self.print_fn(met.metrics_report())
|
||||
|
||||
return min(runtimes) / 10.0
|
||||
except RuntimeError as e:
|
||||
self.print_fn("Doesn't fit on GPU. {}".format(e))
|
||||
if trace_memory:
|
||||
return "N/A", None
|
||||
return "N/A"
|
||||
|
||||
def _measure_memory(self, func):
|
||||
try:
|
||||
if self.args.trace_memory_line_by_line:
|
||||
trace = start_memory_tracing("transformers")
|
||||
|
||||
if self.args.is_tpu:
|
||||
# tpu
|
||||
raise NotImplementedError(
|
||||
"Memory Benchmarking is currently not implemented for TPU. Please disable memory benchmarking with `args.no_memory=True`"
|
||||
)
|
||||
elif self.args.is_gpu:
|
||||
if not is_py3nvml_available():
|
||||
logger.warning(
|
||||
"py3nvml not installed, we won't log GPU memory usage. "
|
||||
"Install py3nvml (pip install py3nvml) to log information about GPU."
|
||||
)
|
||||
memory = "N/A"
|
||||
else:
|
||||
logger.info(
|
||||
"Measuring total GPU usage on GPU device. Make sure to not have additional processes running on the same GPU."
|
||||
)
|
||||
# init nvml
|
||||
nvml.nvmlInit()
|
||||
func()
|
||||
handle = nvml.nvmlDeviceGetHandleByIndex(self.args.device_idx)
|
||||
meminfo = nvml.nvmlDeviceGetMemoryInfo(handle)
|
||||
max_bytes_in_use = meminfo.used
|
||||
memory = Memory(max_bytes_in_use)
|
||||
# shutdown nvml
|
||||
nvml.nvmlShutdown()
|
||||
else:
|
||||
return "N/A"
|
||||
# cpu
|
||||
memory_bytes = measure_peak_memory_cpu(func)
|
||||
memory = Memory(memory_bytes) if isinstance(memory_bytes, int) else memory_bytes
|
||||
|
||||
if self.args.trace_memory_line_by_line:
|
||||
summary = stop_memory_tracing(trace)
|
||||
else:
|
||||
summary = None
|
||||
|
||||
return memory, summary
|
||||
except RuntimeError as e:
|
||||
self.print_fn("Doesn't fit on GPU. {}".format(e))
|
||||
return "N/A", None
|
||||
@@ -34,11 +34,17 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
@dataclass
|
||||
class PyTorchBenchmarkArguments(BenchmarkArguments):
|
||||
no_cuda: bool = field(default=False, metadata={"help": "Whether to run on available cuda devices"})
|
||||
torchscript: bool = field(default=False, metadata={"help": "Trace the models using torchscript"})
|
||||
no_tpu: bool = field(default=False, metadata={"help": "Whether to run on available tpu devices"})
|
||||
fp16: bool = field(default=False, metadata={"help": "Use FP16 to accelerate inference."})
|
||||
tpu_print_metrics: bool = field(default=False, metadata={"help": "Use FP16 to accelerate inference."})
|
||||
torch_xla_tpu_print_metrics: bool = field(default=False, metadata={"help": "Print Xla/PyTorch tpu metrics"})
|
||||
fp16_opt_level: str = field(
|
||||
default="O1",
|
||||
metadata={
|
||||
"help": (
|
||||
"For fp16: Apex AMP optimization level selected in ['O0', 'O1', 'O2', and 'O3']."
|
||||
"See details at https://nvidia.github.io/apex/amp.html"
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@cached_property
|
||||
@torch_required
|
||||
@@ -55,9 +61,14 @@ class PyTorchBenchmarkArguments(BenchmarkArguments):
|
||||
n_gpu = torch.cuda.device_count()
|
||||
return device, n_gpu
|
||||
|
||||
@property
|
||||
def is_tpu(self):
|
||||
return is_torch_tpu_available() and not self.no_tpu
|
||||
|
||||
@property
|
||||
@torch_required
|
||||
def device_idx(self) -> int:
|
||||
# TODO(PVP): currently only single GPU is supported
|
||||
return torch.cuda.current_device()
|
||||
|
||||
@property
|
||||
@@ -69,3 +80,7 @@ class PyTorchBenchmarkArguments(BenchmarkArguments):
|
||||
@torch_required
|
||||
def n_gpu(self):
|
||||
return self._setup_devices[1]
|
||||
|
||||
@property
|
||||
def is_gpu(self):
|
||||
return self.n_gpu > 0
|
||||
@@ -0,0 +1,105 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2018 The HuggingFace Inc. team.
|
||||
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Tuple
|
||||
|
||||
from ..file_utils import cached_property, is_tf_available, tf_required
|
||||
from .benchmark_args_utils import BenchmarkArguments
|
||||
|
||||
|
||||
if is_tf_available():
|
||||
import tensorflow as tf
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TensorflowBenchmarkArguments(BenchmarkArguments):
|
||||
tpu_name: str = field(
|
||||
default=None, metadata={"help": "Name of TPU"},
|
||||
)
|
||||
device_idx: int = field(
|
||||
default=0, metadata={"help": "CPU / GPU device index. Defaults to 0."},
|
||||
)
|
||||
eager_mode: bool = field(default=False, metadata={"help": "Benchmark models in eager model."})
|
||||
use_xla: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"help": "Benchmark models using XLA JIT compilation. Note that `eager_model` has to be set to `False`."
|
||||
},
|
||||
)
|
||||
|
||||
@cached_property
|
||||
@tf_required
|
||||
def _setup_tpu(self) -> Tuple["tf.distribute.cluster_resolver.TPUClusterResolver"]:
|
||||
if not self.no_tpu:
|
||||
try:
|
||||
if self.tpu_name:
|
||||
tpu = tf.distribute.cluster_resolver.TPUClusterResolver(self.tpu_name)
|
||||
else:
|
||||
tpu = tf.distribute.cluster_resolver.TPUClusterResolver()
|
||||
except ValueError:
|
||||
tpu = None
|
||||
return tpu
|
||||
|
||||
@cached_property
|
||||
@tf_required
|
||||
def _setup_strategy(self) -> Tuple["tf.distribute.Strategy", "tf.distribute.cluster_resolver.TPUClusterResolver"]:
|
||||
if self.is_tpu:
|
||||
tf.config.experimental_connect_to_cluster(self._setup_tpu)
|
||||
tf.tpu.experimental.initialize_tpu_system(self._setup_tpu)
|
||||
|
||||
strategy = tf.distribute.experimental.TPUStrategy(self._setup_tpu)
|
||||
else:
|
||||
# currently no multi gpu is allowed
|
||||
if self.is_gpu:
|
||||
# TODO: Currently only single GPU is supported
|
||||
tf.config.experimental.set_visible_devices(self.gpu_list[self.device_idx], "GPU")
|
||||
strategy = tf.distribute.OneDeviceStrategy(device=f"/gpu:{self.device_idx}")
|
||||
else:
|
||||
tf.config.experimental.set_visible_devices([], "GPU") # disable GPU
|
||||
strategy = tf.distribute.OneDeviceStrategy(device=f"/cpu:{self.device_idx}")
|
||||
|
||||
return strategy
|
||||
|
||||
@property
|
||||
@tf_required
|
||||
def is_tpu(self) -> bool:
|
||||
return self._setup_tpu is not None
|
||||
|
||||
@property
|
||||
@tf_required
|
||||
def strategy(self) -> "tf.distribute.Strategy":
|
||||
return self._setup_strategy
|
||||
|
||||
@property
|
||||
@tf_required
|
||||
def gpu_list(self):
|
||||
return tf.config.list_physical_devices("GPU")
|
||||
|
||||
@property
|
||||
@tf_required
|
||||
def n_gpu(self) -> int:
|
||||
if not self.no_cuda:
|
||||
return len(self.gpu_list)
|
||||
return 0
|
||||
|
||||
@property
|
||||
def is_gpu(self) -> bool:
|
||||
return self.n_gpu > 0
|
||||
@@ -16,11 +16,15 @@
|
||||
|
||||
import dataclasses
|
||||
import json
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from time import time
|
||||
from typing import List
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def list_field(default=None, metadata=None):
|
||||
return field(default_factory=lambda: default, metadata=metadata)
|
||||
|
||||
@@ -53,6 +57,9 @@ class BenchmarkArguments:
|
||||
)
|
||||
|
||||
no_inference: bool = field(default=False, metadata={"help": "Don't benchmark inference of model"})
|
||||
no_cuda: bool = field(default=False, metadata={"help": "Whether to run on available cuda devices"})
|
||||
no_tpu: bool = field(default=False, metadata={"help": "Whether to run on available tpu devices"})
|
||||
fp16: bool = field(default=False, metadata={"help": "Use FP16 to accelerate inference."})
|
||||
training: bool = field(default=False, metadata={"help": "Benchmark training of model"})
|
||||
verbose: bool = field(default=False, metadata={"help": "Verbose memory tracing"})
|
||||
no_speed: bool = field(default=False, metadata={"help": "Don't perform speed measurments"})
|
||||
@@ -61,6 +68,12 @@ class BenchmarkArguments:
|
||||
save_to_csv: bool = field(default=False, metadata={"help": "Save result to a CSV file"})
|
||||
log_print: bool = field(default=False, metadata={"help": "Save all print statements in a log file"})
|
||||
no_env_print: bool = field(default=False, metadata={"help": "Don't print environment information"})
|
||||
no_multi_process: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"help": "Don't use multiprocessing for memory and speed measurement. It is highly recommended to use multiprocessing for accurate CPU and GPU memory measurements. This option should only be used for debugging / testing and on TPU."
|
||||
},
|
||||
)
|
||||
with_lm_head: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
@@ -102,3 +115,13 @@ class BenchmarkArguments:
|
||||
@property
|
||||
def model_names(self):
|
||||
return self.models
|
||||
|
||||
@property
|
||||
def do_multi_processing(self):
|
||||
if self.no_multi_process:
|
||||
return False
|
||||
elif self.is_tpu:
|
||||
logger.info("Multiprocessing is currently not possible on TPU.")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -0,0 +1,216 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2018 The HuggingFace Inc. team.
|
||||
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
Benchmarking the library on inference and training in PyTorch.
|
||||
"""
|
||||
|
||||
|
||||
import logging
|
||||
import random
|
||||
import timeit
|
||||
from functools import wraps
|
||||
|
||||
from transformers import (
|
||||
TF_MODEL_MAPPING,
|
||||
TF_MODEL_WITH_LM_HEAD_MAPPING,
|
||||
PretrainedConfig,
|
||||
is_py3nvml_available,
|
||||
is_tf_available,
|
||||
)
|
||||
|
||||
from .benchmark_utils import Benchmark, Memory, measure_peak_memory_cpu, start_memory_tracing, stop_memory_tracing
|
||||
|
||||
|
||||
if is_tf_available():
|
||||
import tensorflow as tf
|
||||
from .benchmark_args_tf import TensorflowBenchmarkArguments
|
||||
from tensorflow.python.framework.errors_impl import ResourceExhaustedError
|
||||
|
||||
if is_py3nvml_available():
|
||||
import py3nvml.py3nvml as nvml
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def run_with_tf_optimizations(do_eager_mode, do_xla):
|
||||
def run_func(func):
|
||||
@wraps(func)
|
||||
def run_in_eager_mode(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@wraps(func)
|
||||
@tf.function(experimental_compile=do_xla)
|
||||
def run_in_graph_mode(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
if do_eager_mode is True:
|
||||
assert (
|
||||
do_xla is False
|
||||
), "Cannot run model in XLA, if `args.eager_mode` is set to `True`. Please set `args.eager_mode=False`."
|
||||
return run_in_eager_mode
|
||||
else:
|
||||
return run_in_graph_mode
|
||||
|
||||
return run_func
|
||||
|
||||
|
||||
def random_input_ids(batch_size, sequence_length, vocab_size):
|
||||
rng = random.Random()
|
||||
values = [rng.randint(0, vocab_size - 1) for i in range(batch_size * sequence_length)]
|
||||
return tf.constant(values, shape=(batch_size, sequence_length), dtype=tf.int32)
|
||||
|
||||
|
||||
class TensorflowBenchmark(Benchmark):
|
||||
|
||||
args: TensorflowBenchmarkArguments
|
||||
configs: PretrainedConfig
|
||||
framework: str = "Tensorflow"
|
||||
|
||||
@property
|
||||
def framework_version(self):
|
||||
return tf.__version__
|
||||
|
||||
def _inference_speed(self, model_name, batch_size, sequence_length):
|
||||
# initialize GPU on separate process
|
||||
strategy = self.args.strategy
|
||||
assert strategy is not None, "A device strategy has to be initialized before using Tensorflow."
|
||||
_inference = self._prepare_inference_func(model_name, batch_size, sequence_length)
|
||||
return self._measure_speed(_inference)
|
||||
|
||||
def _train_speed(self, model_name, batch_size, sequence_length):
|
||||
raise NotImplementedError(
|
||||
"Training is currently not really implemented." "Wait for TFTrainer to support CLM and MLM."
|
||||
)
|
||||
|
||||
def _inference_memory(self, model_name, batch_size, sequence_length):
|
||||
# initialize GPU on separate process
|
||||
if self.args.is_gpu:
|
||||
tf.config.experimental.set_memory_growth(self.args.gpu_list[self.args.device_idx], True)
|
||||
strategy = self.args.strategy
|
||||
assert strategy is not None, "A device strategy has to be initialized before using Tensorflow."
|
||||
_inference = self._prepare_inference_func(model_name, batch_size, sequence_length)
|
||||
return self._measure_memory(_inference)
|
||||
|
||||
def _train_memory(self, model_name, batch_size, sequence_length):
|
||||
raise NotImplementedError(
|
||||
"Training is currently not really implemented. Wait for TFTrainer to support CLM and MLM."
|
||||
)
|
||||
|
||||
def _prepare_inference_func(self, model_name, batch_size, sequence_length):
|
||||
config = self.config_dict[model_name]
|
||||
|
||||
if self.args.fp16:
|
||||
raise NotImplementedError("Mixed precision is currently not supported.")
|
||||
|
||||
if self.args.with_lm_head:
|
||||
model = TF_MODEL_WITH_LM_HEAD_MAPPING[config.__class__](config)
|
||||
else:
|
||||
model = TF_MODEL_MAPPING[config.__class__](config)
|
||||
|
||||
# encoder-decoder has vocab size saved differently
|
||||
vocab_size = config.vocab_size if hasattr(config, "vocab_size") else config.encoder.vocab_size
|
||||
input_ids = random_input_ids(batch_size, sequence_length, vocab_size)
|
||||
|
||||
@run_with_tf_optimizations(self.args.eager_mode, self.args.use_xla)
|
||||
def encoder_decoder_forward():
|
||||
return model(input_ids, decoder_input_ids=input_ids, training=False)
|
||||
|
||||
@run_with_tf_optimizations(self.args.eager_mode, self.args.use_xla)
|
||||
def encoder_forward():
|
||||
return model(input_ids, training=False)
|
||||
|
||||
_inference = encoder_decoder_forward if config.is_encoder_decoder else encoder_forward
|
||||
|
||||
return _inference
|
||||
|
||||
def _measure_speed(self, func):
|
||||
with self.args.strategy.scope():
|
||||
try:
|
||||
if self.args.is_tpu or self.args.use_xla:
|
||||
# run additional 10 times to stabilize compilation for tpu
|
||||
logger.info("Do inference on TPU. Running model 5 times to stabilize compilation")
|
||||
timeit.repeat(func, repeat=1, number=5)
|
||||
|
||||
# as written in https://docs.python.org/2/library/timeit.html#timeit.Timer.repeat, min should be taken rather than the average
|
||||
runtimes = timeit.repeat(func, repeat=self.args.repeat, number=10,)
|
||||
|
||||
return min(runtimes) / 10.0
|
||||
except ResourceExhaustedError as e:
|
||||
self.print_fn("Doesn't fit on GPU. {}".format(e))
|
||||
|
||||
def _measure_memory(self, func):
|
||||
logger.info(
|
||||
"Note that Tensorflow allocates more memory than"
|
||||
"it might need to speed up computation."
|
||||
"The memory reported here corresponds to the memory"
|
||||
"reported by `nvidia-smi`, which can vary depending"
|
||||
"on total available memory on the GPU that is used."
|
||||
)
|
||||
with self.args.strategy.scope():
|
||||
try:
|
||||
if self.args.trace_memory_line_by_line:
|
||||
assert (
|
||||
self.args.eager_mode
|
||||
), "`args.eager_mode` is set to `False`. Make sure to run model in eager mode to measure memory consumption line by line."
|
||||
trace = start_memory_tracing("transformers")
|
||||
|
||||
if self.args.is_tpu:
|
||||
# tpu
|
||||
raise NotImplementedError(
|
||||
"Memory Benchmarking is currently not implemented for TPU. Please disable memory benchmarking with `args.no_memory=True`"
|
||||
)
|
||||
elif self.args.is_gpu:
|
||||
# gpu
|
||||
if not is_py3nvml_available():
|
||||
logger.warning(
|
||||
"py3nvml not installed, we won't log GPU memory usage. "
|
||||
"Install py3nvml (pip install py3nvml) to log information about GPU."
|
||||
)
|
||||
memory = "N/A"
|
||||
else:
|
||||
logger.info(
|
||||
"Measuring total GPU usage on GPU device. Make sure to not have additional processes running on the same GPU."
|
||||
)
|
||||
# init nvml
|
||||
nvml.nvmlInit()
|
||||
func()
|
||||
handle = nvml.nvmlDeviceGetHandleByIndex(self.args.device_idx)
|
||||
meminfo = nvml.nvmlDeviceGetMemoryInfo(handle)
|
||||
max_bytes_in_use = meminfo.used
|
||||
memory = Memory(max_bytes_in_use)
|
||||
# shutdown nvml
|
||||
nvml.nvmlShutdown()
|
||||
else:
|
||||
# cpu
|
||||
if self.args.trace_memory_line_by_line:
|
||||
logger.info(
|
||||
"When enabling line by line tracing, the max peak memory for CPU is inaccurate in Tensorflow."
|
||||
)
|
||||
memory = None
|
||||
else:
|
||||
memory_bytes = measure_peak_memory_cpu(func)
|
||||
memory = Memory(memory_bytes) if isinstance(memory_bytes, int) else memory_bytes
|
||||
if self.args.trace_memory_line_by_line:
|
||||
summary = stop_memory_tracing(trace)
|
||||
if memory is None:
|
||||
memory = summary.total
|
||||
else:
|
||||
summary = None
|
||||
|
||||
return memory, summary
|
||||
except ResourceExhaustedError as e:
|
||||
self.print_fn("Doesn't fit on GPU. {}".format(e))
|
||||
return "N/A", None
|
||||
@@ -14,15 +14,14 @@ import sys
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict, namedtuple
|
||||
from datetime import datetime
|
||||
from multiprocessing import Pipe, Process
|
||||
from multiprocessing import Pipe, Process, Queue
|
||||
from multiprocessing.connection import Connection
|
||||
from signal import SIGKILL
|
||||
from typing import Callable, Iterable, List, NamedTuple, Optional, Union
|
||||
|
||||
from transformers import AutoConfig, PretrainedConfig
|
||||
from transformers import __version__ as version
|
||||
|
||||
from ..file_utils import is_tf_available, is_torch_available, is_torch_tpu_available
|
||||
from ..file_utils import is_psutil_available, is_py3nvml_available, is_tf_available, is_torch_available
|
||||
from .benchmark_args_utils import BenchmarkArguments
|
||||
|
||||
|
||||
@@ -32,6 +31,17 @@ if is_torch_available():
|
||||
if is_tf_available():
|
||||
from tensorflow.python.eager import context as tf_context
|
||||
|
||||
if is_psutil_available():
|
||||
import psutil
|
||||
|
||||
if is_py3nvml_available():
|
||||
import py3nvml.py3nvml as nvml
|
||||
|
||||
if platform.system() == "Windows":
|
||||
from signal import CTRL_C_EVENT as SIGKILL
|
||||
else:
|
||||
from signal import SIGKILL
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
||||
|
||||
@@ -51,6 +61,33 @@ BenchmarkOutput = namedtuple(
|
||||
)
|
||||
|
||||
|
||||
def separate_process_wrapper_fn(func, do_multi_processing):
|
||||
def multi_process_func(*args, **kwargs):
|
||||
# run function in an individual
|
||||
# process to get correct memory
|
||||
def wrapper_func(queue, *args):
|
||||
try:
|
||||
result = func(*args)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
print(e)
|
||||
result = "N/A"
|
||||
queue.put(result)
|
||||
|
||||
queue = Queue()
|
||||
p = Process(target=wrapper_func, args=[queue] + list(args))
|
||||
p.start()
|
||||
result = queue.get()
|
||||
p.join()
|
||||
return result
|
||||
|
||||
if do_multi_processing:
|
||||
logging.info("Use multiprocessing...")
|
||||
return multi_process_func
|
||||
else:
|
||||
return func
|
||||
|
||||
|
||||
def is_memory_tracing_enabled():
|
||||
global _is_memory_tracing_enabled
|
||||
return _is_memory_tracing_enabled
|
||||
@@ -131,7 +168,7 @@ class MemorySummary(NamedTuple):
|
||||
MemoryTrace = List[UsedMemoryState]
|
||||
|
||||
|
||||
def measure_peak_memory_cpu(function: Callable[[], None], interval=0.5) -> int:
|
||||
def measure_peak_memory_cpu(function: Callable[[], None], interval=0.5, device_idx=None) -> int:
|
||||
"""
|
||||
measures peak cpu memory consumption of a given `function`
|
||||
running the function for at least interval seconds
|
||||
@@ -143,16 +180,38 @@ def measure_peak_memory_cpu(function: Callable[[], None], interval=0.5) -> int:
|
||||
- `function`: (`callable`): function() -> ...
|
||||
function without any arguments to measure for which to measure the peak memory
|
||||
|
||||
- `interval`: (`float`)
|
||||
- `interval`: (`float`, `optional`, defaults to `0.5`)
|
||||
interval in second for which to measure the memory usage
|
||||
|
||||
- `device_idx`: (`int`, `optional`, defaults to `None`)
|
||||
device id for which to measure gpu usage
|
||||
|
||||
Returns:
|
||||
- `max_memory`: (`int`)
|
||||
cosumed memory peak in Bytes
|
||||
"""
|
||||
try:
|
||||
import psutil
|
||||
except (ImportError):
|
||||
|
||||
def get_cpu_memory(process_id: int) -> int:
|
||||
"""
|
||||
measures current cpu memory usage of a given `process_id`
|
||||
|
||||
Args:
|
||||
- `process_id`: (`int`)
|
||||
process_id for which to measure memory
|
||||
|
||||
Returns
|
||||
- `memory`: (`int`)
|
||||
cosumed memory in Bytes
|
||||
"""
|
||||
process = psutil.Process(process_id)
|
||||
try:
|
||||
meminfo_attr = "memory_info" if hasattr(process, "memory_info") else "get_memory_info"
|
||||
memory = getattr(process, meminfo_attr)()[0]
|
||||
except psutil.AccessDenied:
|
||||
raise ValueError("Error with Psutil.")
|
||||
return memory
|
||||
|
||||
if not is_psutil_available():
|
||||
logger.warning(
|
||||
"Psutil not installed, we won't log CPU memory usage. "
|
||||
"Install Psutil (pip install psutil) to use CPU memory tracing."
|
||||
@@ -160,26 +219,6 @@ def measure_peak_memory_cpu(function: Callable[[], None], interval=0.5) -> int:
|
||||
max_memory = "N/A"
|
||||
else:
|
||||
|
||||
def _get_memory(process_id: int) -> int:
|
||||
"""
|
||||
measures current cpu memory usage of a given `process_id`
|
||||
|
||||
Args:
|
||||
- `process_id`: (`int`)
|
||||
process_id for which to measure memory
|
||||
|
||||
Returns
|
||||
- `memory`: (`int`)
|
||||
cosumed memory in Bytes
|
||||
"""
|
||||
process = psutil.Process(process_id)
|
||||
try:
|
||||
meminfo_attr = "memory_info" if hasattr(process, "memory_info") else "get_memory_info"
|
||||
memory = getattr(process, meminfo_attr)()[0]
|
||||
except psutil.AccessDenied:
|
||||
raise ValueError("Error with Psutil.")
|
||||
return memory
|
||||
|
||||
class MemoryMeasureProcess(Process):
|
||||
|
||||
"""
|
||||
@@ -193,13 +232,13 @@ def measure_peak_memory_cpu(function: Callable[[], None], interval=0.5) -> int:
|
||||
self.interval = interval
|
||||
self.connection = child_connection
|
||||
self.num_measurements = 1
|
||||
self.mem_usage = _get_memory(process_id)
|
||||
self.mem_usage = get_cpu_memory(self.process_id)
|
||||
|
||||
def run(self):
|
||||
self.connection.send(0)
|
||||
stop = False
|
||||
while True:
|
||||
self.mem_usage = max(self.mem_usage, _get_memory(self.process_id))
|
||||
self.mem_usage = max(self.mem_usage, get_cpu_memory(self.process_id))
|
||||
self.num_measurements += 1
|
||||
|
||||
if stop:
|
||||
@@ -291,34 +330,31 @@ def start_memory_tracing(
|
||||
- 'line_text' (string): Text of the line in the python script
|
||||
|
||||
"""
|
||||
try:
|
||||
import psutil
|
||||
except (ImportError):
|
||||
if is_psutil_available():
|
||||
process = psutil.Process(os.getpid())
|
||||
else:
|
||||
logger.warning(
|
||||
"Psutil not installed, we won't log CPU memory usage. "
|
||||
"Install psutil (pip install psutil) to use CPU memory tracing."
|
||||
)
|
||||
process = None
|
||||
|
||||
if is_py3nvml_available():
|
||||
try:
|
||||
nvml.nvmlInit()
|
||||
devices = list(range(nvml.nvmlDeviceGetCount())) if gpus_to_trace is None else gpus_to_trace
|
||||
nvml.nvmlShutdown()
|
||||
except (OSError, nvml.NVMLError):
|
||||
logger.warning("Error while initializing comunication with GPU. " "We won't perform GPU memory tracing.")
|
||||
log_gpu = False
|
||||
else:
|
||||
log_gpu = is_torch_available() or is_tf_available()
|
||||
else:
|
||||
process = psutil.Process(os.getpid())
|
||||
|
||||
try:
|
||||
from py3nvml import py3nvml
|
||||
|
||||
py3nvml.nvmlInit()
|
||||
devices = list(range(py3nvml.nvmlDeviceGetCount())) if gpus_to_trace is None else gpus_to_trace
|
||||
py3nvml.nvmlShutdown()
|
||||
except ImportError:
|
||||
logger.warning(
|
||||
"py3nvml not installed, we won't log GPU memory usage. "
|
||||
"Install py3nvml (pip install py3nvml) to use GPU memory tracing."
|
||||
)
|
||||
log_gpu = False
|
||||
except (OSError, py3nvml.NVMLError):
|
||||
logger.warning("Error while initializing comunication with GPU. " "We won't perform GPU memory tracing.")
|
||||
log_gpu = False
|
||||
else:
|
||||
log_gpu = is_torch_available() or is_tf_available()
|
||||
|
||||
memory_trace = []
|
||||
|
||||
@@ -380,14 +416,14 @@ def start_memory_tracing(
|
||||
tf_context.context()._clear_caches() # See https://github.com/tensorflow/tensorflow/issues/20218#issuecomment-416771802
|
||||
|
||||
# Sum used memory for all GPUs
|
||||
py3nvml.nvmlInit()
|
||||
nvml.nvmlInit()
|
||||
|
||||
for i in devices:
|
||||
handle = py3nvml.nvmlDeviceGetHandleByIndex(i)
|
||||
meminfo = py3nvml.nvmlDeviceGetMemoryInfo(handle)
|
||||
handle = nvml.nvmlDeviceGetHandleByIndex(i)
|
||||
meminfo = nvml.nvmlDeviceGetMemoryInfo(handle)
|
||||
gpu_mem += meminfo.used
|
||||
|
||||
py3nvml.nvmlShutdown()
|
||||
nvml.nvmlShutdown()
|
||||
|
||||
mem_state = UsedMemoryState(traced_state, cpu_mem, gpu_mem)
|
||||
memory_trace.append(mem_state)
|
||||
@@ -517,7 +553,6 @@ class Benchmark(ABC):
|
||||
|
||||
def __init__(self, args: BenchmarkArguments = None, configs: PretrainedConfig = None):
|
||||
self.args = args
|
||||
|
||||
if configs is None:
|
||||
self.config_dict = {
|
||||
model_name: AutoConfig.from_pretrained(model_name) for model_name in self.args.model_names
|
||||
@@ -525,6 +560,11 @@ class Benchmark(ABC):
|
||||
else:
|
||||
self.config_dict = {model_name: config for model_name, config in zip(self.args.model_names, configs)}
|
||||
|
||||
if not self.args.no_memory and os.getenv("TRANSFORMERS_USE_MULTIPROCESSING") == 0:
|
||||
logger.warning(
|
||||
"Memory consumption will not be measured accurately if `args.no_multi_process` is set to `True.` The flag 'TRANSFORMERS_USE_MULTIPROCESSING' should only be disabled for debugging / testing."
|
||||
)
|
||||
|
||||
self._print_fn = None
|
||||
self._framework_version = None
|
||||
self._environment_info = None
|
||||
@@ -536,7 +576,7 @@ class Benchmark(ABC):
|
||||
|
||||
def print_and_log(*args):
|
||||
with open(self.args.log_filename, "a") as log_file:
|
||||
log_file.write(str(*args) + "\n")
|
||||
log_file.write("".join(args) + "\n")
|
||||
print(*args)
|
||||
|
||||
self._print_fn = print_and_log
|
||||
@@ -544,27 +584,39 @@ class Benchmark(ABC):
|
||||
self._print_fn = print
|
||||
return self._print_fn
|
||||
|
||||
@property
|
||||
def is_gpu(self):
|
||||
return self.args.n_gpu > 0
|
||||
|
||||
@property
|
||||
def is_tpu(self):
|
||||
return is_torch_tpu_available() and not self.args.no_tpu
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def framework_version(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def train(self, model_name, batch_size, sequence_length):
|
||||
def _inference_speed(self, model_name, batch_size, sequence_length):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def inference(self, model_name, batch_size, sequence_length):
|
||||
def _train_speed(self, model_name, batch_size, sequence_length):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def _inference_memory(self, model_name, batch_size, sequence_length):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def _train_memory(self, model_name, batch_size, sequence_length):
|
||||
pass
|
||||
|
||||
def inference_speed(self, *args, **kwargs):
|
||||
return separate_process_wrapper_fn(self._inference_speed, self.args.do_multi_processing)(*args, **kwargs)
|
||||
|
||||
def train_speed(self, *args, **kwargs):
|
||||
return separate_process_wrapper_fn(self._train_speed, self.args.do_multi_processing)(*args, **kwargs)
|
||||
|
||||
def inference_memory(self, *args, **kwargs):
|
||||
return separate_process_wrapper_fn(self._inference_memory, self.args.do_multi_processing)(*args, **kwargs)
|
||||
|
||||
def train_memory(self, *args, **kwargs):
|
||||
return separate_process_wrapper_fn(self._train_memory, self.args.do_multi_processing)(*args, **kwargs)
|
||||
|
||||
def run(self):
|
||||
result_dict = {model_name: {} for model_name in self.args.model_names}
|
||||
inference_result_time = copy.deepcopy(result_dict)
|
||||
@@ -591,64 +643,60 @@ class Benchmark(ABC):
|
||||
for sequence_length in self.args.sequence_lengths:
|
||||
if not self.args.no_inference:
|
||||
if not self.args.no_memory:
|
||||
memory, inference_summary = self.inference(
|
||||
model_name, batch_size, sequence_length, trace_memory=True
|
||||
)
|
||||
memory, inference_summary = self.inference_memory(model_name, batch_size, sequence_length)
|
||||
inference_result_memory[model_name]["result"][batch_size][sequence_length] = memory
|
||||
if not self.args.no_speed:
|
||||
time = self.inference(model_name, batch_size, sequence_length, trace_memory=False)
|
||||
time = self.inference_speed(model_name, batch_size, sequence_length)
|
||||
inference_result_time[model_name]["result"][batch_size][sequence_length] = time
|
||||
|
||||
if self.args.training:
|
||||
if not self.args.no_memory:
|
||||
memory, train_summary = self.train(
|
||||
model_name, batch_size, sequence_length, trace_memory=True
|
||||
)
|
||||
memory, train_summary = self.train_memory(model_name, batch_size, sequence_length)
|
||||
train_result_memory[model_name]["result"][batch_size][sequence_length] = memory
|
||||
if not self.args.no_speed:
|
||||
time = self.inference(model_name, batch_size, sequence_length, trace_memory=False)
|
||||
time = self.train_speed(model_name, batch_size, sequence_length)
|
||||
train_result_time[model_name]["result"][batch_size][sequence_length] = time
|
||||
|
||||
if not self.args.no_inference:
|
||||
if not self.args.no_speed:
|
||||
self.print_fn("======= INFERENCE - SPEED - RESULT =======")
|
||||
self.print_results(inference_result_time)
|
||||
self.print_fn("\n" + 20 * "=" + ("INFERENCE - SPEED - RESULT").center(40) + 20 * "=")
|
||||
self.print_results(inference_result_time, type_label="Time in s")
|
||||
self.save_to_csv(inference_result_time, self.args.inference_time_csv_file)
|
||||
if self.is_tpu:
|
||||
if self.args.is_tpu:
|
||||
self.print_fn(
|
||||
"TPU was used for inference. Note that the time after compilation stabilized (after ~10 inferences model.forward(..) calls) was measured."
|
||||
)
|
||||
|
||||
if not self.args.no_memory:
|
||||
self.print_fn("======= INFERENCE - MEMORY - RESULT =======")
|
||||
self.print_results(inference_result_memory)
|
||||
self.print_fn("\n" + 20 * "=" + ("INFERENCE - MEMORY - RESULT").center(40) + 20 * "=")
|
||||
self.print_results(inference_result_memory, type_label="Memory in MB")
|
||||
self.save_to_csv(inference_result_memory, self.args.inference_memory_csv_file)
|
||||
|
||||
if self.args.trace_memory_line_by_line:
|
||||
self.print_fn("======= INFERENCE - MEMORY LINE BY LINE TRACE - SUMMARY =======")
|
||||
self.print_fn("\n" + 20 * "=" + ("INFERENCE - MEMOMRY - LINE BY LINE - SUMMARY").center(40) + 20 * "=")
|
||||
self.print_memory_trace_statistics(inference_summary)
|
||||
|
||||
if self.args.training:
|
||||
if not self.args.no_speed:
|
||||
self.print_fn("======= TRAIN - SPEED - RESULT =======")
|
||||
self.print_results(train_result_time)
|
||||
self.print_fn("\n" + 20 * "=" + ("TRAIN - SPEED - RESULTS").center(40) + 20 * "=")
|
||||
self.print_results(train_result_time, "Time in s")
|
||||
self.save_to_csv(train_result_time, self.args.train_time_csv_file)
|
||||
if self.is_tpu:
|
||||
if self.args.is_tpu:
|
||||
self.print_fn(
|
||||
"TPU was used for training. Note that the time after compilation stabilized (after ~10 train loss=model.forward(...) + loss.backward() calls) was measured."
|
||||
)
|
||||
|
||||
if not self.args.no_memory:
|
||||
self.print_fn("======= TRAIN - MEMORY - RESULT =======")
|
||||
self.print_results(train_result_memory)
|
||||
self.print_fn("\n" + 20 * "=" + ("TRAIN - MEMORY - RESULTS").center(40) + 20 * "=")
|
||||
self.print_results(train_result_memory, type_label="Memory in MB")
|
||||
self.save_to_csv(train_result_memory, self.args.train_memory_csv_file)
|
||||
|
||||
if self.args.trace_memory_line_by_line:
|
||||
self.print_fn("======= TRAIN - MEMORY LINE BY LINE TRACE - SUMMARY =======")
|
||||
self.print_fn("\n" + 20 * "=" + ("TRAIN - MEMOMRY - LINE BY LINE - SUMMARY").center(40) + 20 * "=")
|
||||
self.print_memory_trace_statistics(train_summary)
|
||||
|
||||
if not self.args.no_env_print:
|
||||
self.print_fn("\n======== ENVIRONMENT - INFORMATION ========")
|
||||
self.print_fn("\n" + 20 * "=" + ("ENVIRONMENT INFORMATION").center(40) + 20 * "=")
|
||||
self.print_fn(
|
||||
"\n".join(["- {}: {}".format(prop, val) for prop, val in self.environment_info.items()]) + "\n"
|
||||
)
|
||||
@@ -676,6 +724,9 @@ class Benchmark(ABC):
|
||||
info["framework"] = self.framework
|
||||
if self.framework == "PyTorch":
|
||||
info["use_torchscript"] = self.args.torchscript
|
||||
if self.framework == "Tensorflow":
|
||||
info["eager_mode"] = self.args.eager_mode
|
||||
info["use_xla"] = self.args.use_xla
|
||||
info["framework_version"] = self.framework_version
|
||||
info["python_version"] = platform.python_version()
|
||||
info["system"] = platform.system()
|
||||
@@ -683,27 +734,30 @@ class Benchmark(ABC):
|
||||
info["architecture"] = platform.architecture()[0]
|
||||
info["date"] = datetime.date(datetime.now())
|
||||
info["time"] = datetime.time(datetime.now())
|
||||
info["fp16"] = self.args.fp16
|
||||
info["use_multiprocessing"] = self.args.do_multi_processing
|
||||
|
||||
try:
|
||||
import psutil
|
||||
except (ImportError):
|
||||
if is_psutil_available():
|
||||
info["cpu_ram_mb"] = bytes_to_mega_bytes(psutil.virtual_memory().total)
|
||||
else:
|
||||
logger.warning(
|
||||
"Psutil not installed, we won't log available CPU memory."
|
||||
"Install psutil (pip install psutil) to log available CPU memory."
|
||||
)
|
||||
info["cpu_ram_mb"] = "N/A"
|
||||
else:
|
||||
info["cpu_ram_mb"] = bytes_to_mega_bytes(psutil.virtual_memory().total)
|
||||
|
||||
info["use_gpu"] = self.is_gpu
|
||||
if self.is_gpu:
|
||||
info["num_gpus"] = self.args.n_gpu
|
||||
try:
|
||||
from py3nvml import py3nvml
|
||||
|
||||
py3nvml.nvmlInit()
|
||||
handle = py3nvml.nvmlDeviceGetHandleByIndex(self.args.device_idx)
|
||||
except ImportError:
|
||||
info["use_gpu"] = self.args.is_gpu
|
||||
if self.args.is_gpu:
|
||||
info["num_gpus"] = 1 # TODO(PVP) Currently only single GPU is supported
|
||||
if is_py3nvml_available():
|
||||
nvml.nvmlInit()
|
||||
handle = nvml.nvmlDeviceGetHandleByIndex(self.args.device_idx)
|
||||
info["gpu"] = nvml.nvmlDeviceGetName(handle)
|
||||
info["gpu_ram_mb"] = bytes_to_mega_bytes(nvml.nvmlDeviceGetMemoryInfo(handle).total)
|
||||
info["gpu_power_watts"] = nvml.nvmlDeviceGetPowerManagementLimit(handle) / 1000
|
||||
info["gpu_performance_state"] = nvml.nvmlDeviceGetPerformanceState(handle)
|
||||
nvml.nvmlShutdown()
|
||||
else:
|
||||
logger.warning(
|
||||
"py3nvml not installed, we won't log GPU memory usage. "
|
||||
"Install py3nvml (pip install py3nvml) to log information about GPU."
|
||||
@@ -712,41 +766,35 @@ class Benchmark(ABC):
|
||||
info["gpu_ram_mb"] = "N/A"
|
||||
info["gpu_power_watts"] = "N/A"
|
||||
info["gpu_performance_state"] = "N/A"
|
||||
except (OSError, py3nvml.NVMLError):
|
||||
logger.warning(
|
||||
"Error while initializing comunication with GPU. " "We won't log information about GPU."
|
||||
)
|
||||
info["gpu"] = "N/A"
|
||||
info["gpu_ram_mb"] = "N/A"
|
||||
info["gpu_power_watts"] = "N/A"
|
||||
info["gpu_performance_state"] = "N/A"
|
||||
py3nvml.nvmlShutdown()
|
||||
else:
|
||||
info["gpu"] = py3nvml.nvmlDeviceGetName(handle)
|
||||
info["gpu_ram_mb"] = bytes_to_mega_bytes(py3nvml.nvmlDeviceGetMemoryInfo(handle).total)
|
||||
info["gpu_power_watts"] = py3nvml.nvmlDeviceGetPowerManagementLimit(handle) / 1000
|
||||
info["gpu_performance_state"] = py3nvml.nvmlDeviceGetPerformanceState(handle)
|
||||
py3nvml.nvmlShutdown()
|
||||
|
||||
info["use_tpu"] = self.is_tpu
|
||||
info["use_tpu"] = self.args.is_tpu
|
||||
# TODO(PVP): See if we can add more information about TPU
|
||||
# see: https://github.com/pytorch/xla/issues/2180
|
||||
|
||||
self._environment_info = info
|
||||
return self._environment_info
|
||||
|
||||
def print_results(self, result_dict):
|
||||
def print_results(self, result_dict, type_label):
|
||||
self.print_fn(80 * "-")
|
||||
self.print_fn(
|
||||
"Model Name".center(30) + "Batch Size".center(15) + "Seq Length".center(15) + type_label.center(15)
|
||||
)
|
||||
self.print_fn(80 * "-")
|
||||
for model_name in self.args.model_names:
|
||||
self.print_fn("\t" + f"======= MODEL CHECKPOINT: {model_name} =======")
|
||||
for batch_size in result_dict[model_name]["bs"]:
|
||||
for sequence_length in result_dict[model_name]["ss"]:
|
||||
result = result_dict[model_name]["result"][batch_size][sequence_length]
|
||||
if isinstance(result, float):
|
||||
self.print_fn(
|
||||
f"\t\t{model_name}/{batch_size}/{sequence_length}: " f"{(round(1000 * result) / 1000)}s"
|
||||
)
|
||||
result = round(1000 * result) / 1000
|
||||
result = "< 0.001" if result == 0.0 else str(result)
|
||||
else:
|
||||
self.print_fn(f"\t\t{model_name}/{batch_size}/{sequence_length}: " f"{result} MB")
|
||||
result = str(result)
|
||||
self.print_fn(
|
||||
model_name.center(30) + str(batch_size).center(15),
|
||||
str(sequence_length).center(15),
|
||||
result.center(15),
|
||||
)
|
||||
self.print_fn(80 * "-")
|
||||
|
||||
def print_memory_trace_statistics(self, summary: MemorySummary):
|
||||
self.print_fn(
|
||||
|
||||
@@ -32,6 +32,7 @@ from .configuration_longformer import LONGFORMER_PRETRAINED_CONFIG_ARCHIVE_MAP,
|
||||
from .configuration_marian import MarianConfig
|
||||
from .configuration_openai import OPENAI_GPT_PRETRAINED_CONFIG_ARCHIVE_MAP, OpenAIGPTConfig
|
||||
from .configuration_reformer import ReformerConfig
|
||||
from .configuration_retribert import RETRIBERT_PRETRAINED_CONFIG_ARCHIVE_MAP, RetriBertConfig
|
||||
from .configuration_roberta import ROBERTA_PRETRAINED_CONFIG_ARCHIVE_MAP, RobertaConfig
|
||||
from .configuration_t5 import T5_PRETRAINED_CONFIG_ARCHIVE_MAP, T5Config
|
||||
from .configuration_transfo_xl import TRANSFO_XL_PRETRAINED_CONFIG_ARCHIVE_MAP, TransfoXLConfig
|
||||
@@ -64,6 +65,7 @@ ALL_PRETRAINED_CONFIG_ARCHIVE_MAP = dict(
|
||||
FLAUBERT_PRETRAINED_CONFIG_ARCHIVE_MAP,
|
||||
ELECTRA_PRETRAINED_CONFIG_ARCHIVE_MAP,
|
||||
LONGFORMER_PRETRAINED_CONFIG_ARCHIVE_MAP,
|
||||
RETRIBERT_PRETRAINED_CONFIG_ARCHIVE_MAP,
|
||||
]
|
||||
for key, value, in pretrained_map.items()
|
||||
)
|
||||
@@ -71,6 +73,7 @@ ALL_PRETRAINED_CONFIG_ARCHIVE_MAP = dict(
|
||||
|
||||
CONFIG_MAPPING = OrderedDict(
|
||||
[
|
||||
("retribert", RetriBertConfig,),
|
||||
("t5", T5Config,),
|
||||
("distilbert", DistilBertConfig,),
|
||||
("albert", AlbertConfig,),
|
||||
@@ -131,6 +134,7 @@ class AutoConfig:
|
||||
The configuration class to instantiate is selected
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
|
||||
- `t5`: :class:`~transformers.T5Config` (T5 model)
|
||||
- `distilbert`: :class:`~transformers.DistilBertConfig` (DistilBERT model)
|
||||
- `albert`: :class:`~transformers.AlbertConfig` (ALBERT model)
|
||||
|
||||
@@ -28,6 +28,7 @@ BART_PRETRAINED_CONFIG_ARCHIVE_MAP = {
|
||||
"facebook/bart-large-cnn": "https://s3.amazonaws.com/models.huggingface.co/bert/facebook/bart-large-cnn/config.json",
|
||||
"facebook/bart-large-xsum": "https://s3.amazonaws.com/models.huggingface.co/bert/facebook/bart-large-xsum/config.json",
|
||||
"facebook/mbart-large-en-ro": "https://s3.amazonaws.com/models.huggingface.co/bert/facebook/mbart-large-en-ro/config.json",
|
||||
"yjernite/bart_eli5": "https://s3.amazonaws.com/models.huggingface.co/bert/yjernite/bart_eli5/config.json",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ class DistilBertConfig(PretrainedConfig):
|
||||
The dropout probabilities used in the question answering model
|
||||
:class:`~transformers.DistilBertForQuestionAnswering`.
|
||||
seq_classif_dropout (:obj:`float`, optional, defaults to 0.2):
|
||||
The dropout probabilities used in the sequence classification model
|
||||
The dropout probabilities used in the sequence classification and the multiple choice model
|
||||
:class:`~transformers.DistilBertForSequenceClassification`.
|
||||
|
||||
Example::
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2019-present, the HuggingFace Inc. team, The Google AI Language Team and Facebook, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
""" RetriBERT model configuration """
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from .configuration_utils import PretrainedConfig
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# TODO: uploadto AWS
|
||||
RETRIBERT_PRETRAINED_CONFIG_ARCHIVE_MAP = {
|
||||
"retribert-base-uncased": "https://s3.amazonaws.com/models.huggingface.co/bert/distilbert-base-uncased-config.json",
|
||||
}
|
||||
|
||||
|
||||
class RetriBertConfig(PretrainedConfig):
|
||||
r"""
|
||||
This is the configuration class to store the configuration of a :class:`~transformers.RetriBertModel`.
|
||||
It is used to instantiate a RetriBertModel model according to the specified arguments, defining the model
|
||||
architecture.
|
||||
|
||||
Configuration objects inherit from :class:`~transformers.PretrainedConfig` and can be used
|
||||
to control the model outputs. Read the documentation from :class:`~transformers.PretrainedConfig`
|
||||
for more information.
|
||||
|
||||
|
||||
Args:
|
||||
vocab_size (:obj:`int`, optional, defaults to 30522):
|
||||
Vocabulary size of the BERT model. Defines the different tokens that
|
||||
can be represented by the `inputs_ids` passed to the forward method of :class:`~transformers.BertModel`.
|
||||
hidden_size (:obj:`int`, optional, defaults to 768):
|
||||
Dimensionality of the encoder layers and the pooler layer.
|
||||
num_hidden_layers (:obj:`int`, optional, defaults to 12):
|
||||
Number of hidden layers in the Transformer encoder.
|
||||
num_attention_heads (:obj:`int`, optional, defaults to 12):
|
||||
Number of attention heads for each attention layer in the Transformer encoder.
|
||||
intermediate_size (:obj:`int`, optional, defaults to 3072):
|
||||
Dimensionality of the "intermediate" (i.e., feed-forward) layer in the Transformer encoder.
|
||||
hidden_act (:obj:`str` or :obj:`function`, optional, defaults to "gelu"):
|
||||
The non-linear activation function (function or string) in the encoder and pooler.
|
||||
If string, "gelu", "relu", "swish" and "gelu_new" are supported.
|
||||
hidden_dropout_prob (:obj:`float`, optional, defaults to 0.1):
|
||||
The dropout probabilitiy for all fully connected layers in the embeddings, encoder, and pooler.
|
||||
attention_probs_dropout_prob (:obj:`float`, optional, defaults to 0.1):
|
||||
The dropout ratio for the attention probabilities.
|
||||
max_position_embeddings (:obj:`int`, optional, defaults to 512):
|
||||
The maximum sequence length that this model might ever be used with.
|
||||
Typically set this to something large just in case (e.g., 512 or 1024 or 2048).
|
||||
type_vocab_size (:obj:`int`, optional, defaults to 2):
|
||||
The vocabulary size of the `token_type_ids` passed into :class:`~transformers.BertModel`.
|
||||
initializer_range (:obj:`float`, optional, defaults to 0.02):
|
||||
The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
|
||||
layer_norm_eps (:obj:`float`, optional, defaults to 1e-12):
|
||||
The epsilon used by the layer normalization layers.
|
||||
share_encoders (:obj:`bool`, optional, defaults to True):
|
||||
Whether to use the same Bert-type encoder for the queries and document
|
||||
projection_dim (:obj:`int`, optional, defaults to 128):
|
||||
Final dimension of the query and document representation after projection
|
||||
|
||||
"""
|
||||
model_type = "retribert"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vocab_size=30522,
|
||||
hidden_size=768,
|
||||
num_hidden_layers=8,
|
||||
num_attention_heads=12,
|
||||
intermediate_size=3072,
|
||||
hidden_act="gelu",
|
||||
hidden_dropout_prob=0.1,
|
||||
attention_probs_dropout_prob=0.1,
|
||||
max_position_embeddings=512,
|
||||
type_vocab_size=2,
|
||||
initializer_range=0.02,
|
||||
layer_norm_eps=1e-12,
|
||||
share_encoders=True,
|
||||
projection_dim=128,
|
||||
pad_token_id=0,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(pad_token_id=pad_token_id, **kwargs)
|
||||
|
||||
self.vocab_size = vocab_size
|
||||
self.hidden_size = hidden_size
|
||||
self.num_hidden_layers = num_hidden_layers
|
||||
self.num_attention_heads = num_attention_heads
|
||||
self.hidden_act = hidden_act
|
||||
self.intermediate_size = intermediate_size
|
||||
self.hidden_dropout_prob = hidden_dropout_prob
|
||||
self.attention_probs_dropout_prob = attention_probs_dropout_prob
|
||||
self.max_position_embeddings = max_position_embeddings
|
||||
self.type_vocab_size = type_vocab_size
|
||||
self.initializer_range = initializer_range
|
||||
self.layer_norm_eps = layer_norm_eps
|
||||
self.share_encoders = share_encoders
|
||||
self.projection_dim = projection_dim
|
||||
@@ -53,7 +53,7 @@ class T5Config(PretrainedConfig):
|
||||
probabilities.
|
||||
n_positions: The maximum sequence length that this model might
|
||||
ever be used with. Typically set this to something large just in case
|
||||
(e.g., 512 or 1024 or 2048). `n_positions` can also be accessed via the property `max_position_embeddings'.
|
||||
(e.g., 512 or 1024 or 2048). `n_positions` can also be accessed via the property `max_position_embeddings`.
|
||||
type_vocab_size: The vocabulary size of the `token_type_ids` passed into
|
||||
`T5Model`.
|
||||
initializer_factor: A factor for initializing all weight matrices (should be kept to 1.0, used for initialization testing).
|
||||
|
||||
@@ -20,7 +20,6 @@ import copy
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import warnings
|
||||
from typing import Dict, Tuple
|
||||
|
||||
from .file_utils import CONFIG_NAME, cached_path, hf_bucket_url, is_remote_url
|
||||
@@ -45,8 +44,10 @@ class PretrainedConfig(object):
|
||||
Name of the task used to fine-tune the model. This can be used when converting from an original (TensorFlow or PyTorch) checkpoint.
|
||||
num_labels (:obj:`int`, `optional`, defaults to `2`):
|
||||
Number of classes to use when the model is a classification model (sequences/tokens)
|
||||
output_hidden_states (:obj:`string`, `optional`, defaults to :obj:`False`):
|
||||
output_hidden_states (:obj:`bool`, `optional`, defaults to :obj:`False`):
|
||||
Should the model returns all hidden-states.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`False`):
|
||||
Should the model returns all attentions.
|
||||
torchscript (:obj:`bool`, `optional`, defaults to :obj:`False`):
|
||||
Is the model used with Torchscript (for PyTorch models).
|
||||
"""
|
||||
@@ -55,13 +56,6 @@ class PretrainedConfig(object):
|
||||
def __init__(self, **kwargs):
|
||||
# Attributes with defaults
|
||||
self.output_hidden_states = kwargs.pop("output_hidden_states", False)
|
||||
|
||||
if "output_attentions" in kwargs:
|
||||
warnings.warn(
|
||||
"The `output_attentions` in the configuration is deprecated and will be removed in a later version. "
|
||||
"Please use `output_attention` as an argument to your model's foward method instead.",
|
||||
FutureWarning,
|
||||
)
|
||||
self.output_attentions = kwargs.pop("output_attentions", False)
|
||||
self.use_cache = kwargs.pop("use_cache", True) # Not used by all models
|
||||
self.torchscript = kwargs.pop("torchscript", False) # Only used by PyTorch models
|
||||
@@ -295,13 +289,6 @@ class PretrainedConfig(object):
|
||||
if hasattr(config, "pruned_heads"):
|
||||
config.pruned_heads = dict((int(key), value) for key, value in config.pruned_heads.items())
|
||||
|
||||
if "output_attentions" in kwargs:
|
||||
warnings.warn(
|
||||
"The `output_attentions` in the configuration is deprecated and will be removed in a later version. "
|
||||
"Please use `output_attention` as an argument to your model's forward method instead.",
|
||||
FutureWarning,
|
||||
)
|
||||
|
||||
# Update config with kwargs if needed
|
||||
to_remove = []
|
||||
for key, value in kwargs.items():
|
||||
|
||||
@@ -84,11 +84,12 @@ class XLNetConfig(PretrainedConfig):
|
||||
Argument used when doing sequence summary. Used in for the multiple choice head in
|
||||
:class:transformers.XLNetForSequenceClassification` and :class:`~transformers.XLNetForMultipleChoice`.
|
||||
Is one of the following options:
|
||||
- 'last' => take the last token hidden state (like XLNet)
|
||||
- 'first' => take the first token hidden state (like Bert)
|
||||
- 'mean' => take the mean of all tokens hidden states
|
||||
- 'cls_index' => supply a Tensor of classification token position (GPT/GPT-2)
|
||||
- 'attn' => Not implemented now, use multi-head attention
|
||||
|
||||
- 'last' => take the last token hidden state (like XLNet)
|
||||
- 'first' => take the first token hidden state (like Bert)
|
||||
- 'mean' => take the mean of all tokens hidden states
|
||||
- 'cls_index' => supply a Tensor of classification token position (GPT/GPT-2)
|
||||
- 'attn' => Not implemented now, use multi-head attention
|
||||
summary_use_proj (:obj:`boolean`, optional, defaults to :obj:`True`):
|
||||
Argument used when doing sequence summary. Used in for the multiple choice head in
|
||||
:class:`~transformers.XLNetForSequenceClassification` and :class:`~transformers.XLNetForMultipleChoice`.
|
||||
|
||||
@@ -8,6 +8,19 @@ from transformers.pipelines import Pipeline, pipeline
|
||||
from transformers.tokenization_utils import BatchEncoding
|
||||
|
||||
|
||||
SUPPORTED_PIPELINES = [
|
||||
"feature-extraction",
|
||||
"ner",
|
||||
"sentiment-analysis",
|
||||
"fill-mask",
|
||||
"question-answering",
|
||||
"text-generation",
|
||||
"translation_en_to_fr",
|
||||
"translation_en_to_de",
|
||||
"translation_en_to_ro",
|
||||
]
|
||||
|
||||
|
||||
class OnnxConverterArgumentParser(ArgumentParser):
|
||||
"""
|
||||
Wraps all the script arguments supported to export transformers models to ONNX IR
|
||||
@@ -16,6 +29,7 @@ class OnnxConverterArgumentParser(ArgumentParser):
|
||||
def __init__(self):
|
||||
super(OnnxConverterArgumentParser, self).__init__("ONNX Converter")
|
||||
|
||||
self.add_argument("--pipeline", type=str, choices=SUPPORTED_PIPELINES, default="feature-extraction")
|
||||
self.add_argument("--model", type=str, required=True, help="Model's id or path (ex: bert-base-cased)")
|
||||
self.add_argument("--tokenizer", type=str, help="Tokenizer's id or path (ex: bert-base-cased)")
|
||||
self.add_argument("--framework", type=str, choices=["pt", "tf"], help="Framework for loading the model")
|
||||
@@ -36,24 +50,26 @@ def ensure_valid_input(model, tokens, input_names):
|
||||
Returns: Tuple
|
||||
|
||||
"""
|
||||
model_args_name = model.forward.__code__.co_varnames
|
||||
print("Ensuring inputs are in correct order")
|
||||
|
||||
ordered_input_names = []
|
||||
model_args = []
|
||||
model_args_name = model.forward.__code__.co_varnames
|
||||
model_args, ordered_input_names = [], []
|
||||
for arg_name in model_args_name[1:]: # start at index 1 to skip "self" argument
|
||||
if arg_name in input_names:
|
||||
ordered_input_names.append(arg_name)
|
||||
model_args.append(tokens[arg_name])
|
||||
else:
|
||||
print("{} is not present in the generated input list.".format(arg_name))
|
||||
break
|
||||
|
||||
print("Generated inputs order: {}".format(ordered_input_names))
|
||||
return ordered_input_names, tuple(model_args)
|
||||
|
||||
|
||||
def infer_shapes(nlp: Pipeline, framework: str) -> Tuple[List[str], List[str], Dict, BatchEncoding]:
|
||||
def build_shape_dict(tensor, is_input: bool, seq_len: int):
|
||||
def build_shape_dict(name: str, tensor, is_input: bool, seq_len: int):
|
||||
if isinstance(tensor, (tuple, list)):
|
||||
return [build_shape_dict(t, is_input, seq_len) for t in tensor]
|
||||
return [build_shape_dict(name, t, is_input, seq_len) for t in tensor]
|
||||
|
||||
else:
|
||||
# Let's assume batch is the first axis with only 1 element (~~ might not be always true ...)
|
||||
@@ -67,6 +83,7 @@ def infer_shapes(nlp: Pipeline, framework: str) -> Tuple[List[str], List[str], D
|
||||
seq_axes = [dim for dim, shape in enumerate(tensor.shape) if shape == seq_len]
|
||||
axes.update({dim: "sequence" for dim in seq_axes})
|
||||
|
||||
print("Found {} {} with shape: {}".format("input" if is_input else "output", name, axes))
|
||||
return axes
|
||||
|
||||
tokens = nlp.tokenizer.encode_plus("This is a sample output", return_tensors=framework)
|
||||
@@ -78,7 +95,7 @@ def infer_shapes(nlp: Pipeline, framework: str) -> Tuple[List[str], List[str], D
|
||||
|
||||
# Generate input names & axes
|
||||
input_vars = list(tokens.keys())
|
||||
input_dynamic_axes = {k: build_shape_dict(v, True, seq_len) for k, v in tokens.items()}
|
||||
input_dynamic_axes = {k: build_shape_dict(k, v, True, seq_len) for k, v in tokens.items()}
|
||||
|
||||
# flatten potentially grouped outputs (past for gpt2, attentions)
|
||||
outputs_flat = []
|
||||
@@ -90,7 +107,7 @@ def infer_shapes(nlp: Pipeline, framework: str) -> Tuple[List[str], List[str], D
|
||||
|
||||
# Generate output names & axes
|
||||
output_names = ["output_{}".format(i) for i in range(len(outputs_flat))]
|
||||
output_dynamic_axes = {k: build_shape_dict(v, False, seq_len) for k, v in zip(output_names, outputs_flat)}
|
||||
output_dynamic_axes = {k: build_shape_dict(k, v, False, seq_len) for k, v in zip(output_names, outputs_flat)}
|
||||
|
||||
# Create the aggregated axes representation
|
||||
dynamic_axes = dict(input_dynamic_axes, **output_dynamic_axes)
|
||||
@@ -105,7 +122,7 @@ def load_graph_from_args(framework: str, model: str, tokenizer: Optional[str] =
|
||||
print("Loading pipeline (model: {}, tokenizer: {})".format(model, tokenizer))
|
||||
|
||||
# Allocate tokenizer and model
|
||||
return pipeline("feature-extraction", model=model, tokenizer=tokenizer, framework=framework)
|
||||
return pipeline(args.pipeline, model=model, tokenizer=tokenizer, framework=framework)
|
||||
|
||||
|
||||
def convert_pytorch(nlp: Pipeline, opset: int, output: str, use_external_format: bool):
|
||||
@@ -115,7 +132,7 @@ def convert_pytorch(nlp: Pipeline, opset: int, output: str, use_external_format:
|
||||
import torch
|
||||
from torch.onnx import export
|
||||
|
||||
print("PyTorch: {}".format(torch.__version__))
|
||||
print("Using framework PyTorch: {}".format(torch.__version__))
|
||||
|
||||
with torch.no_grad():
|
||||
input_names, output_names, dynamic_axes, tokens = infer_shapes(nlp, "pt")
|
||||
@@ -147,7 +164,7 @@ def convert_tensorflow(nlp: Pipeline, opset: int, output: str):
|
||||
import tensorflow as tf
|
||||
from keras2onnx import convert_keras, save_model, __version__ as k2ov
|
||||
|
||||
print("TensorFlow: {}, keras2onnx: {}".format(tf.version.VERSION, k2ov))
|
||||
print("Using framework TensorFlow: {}, keras2onnx: {}".format(tf.version.VERSION, k2ov))
|
||||
|
||||
# Build
|
||||
input_names, output_names, dynamic_axes, tokens = infer_shapes(nlp, "tf")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List, NewType, Tuple
|
||||
from typing import Any, Callable, Dict, List, NewType, Tuple
|
||||
|
||||
import torch
|
||||
from torch.nn.utils.rnn import pad_sequence
|
||||
@@ -8,28 +7,16 @@ from torch.nn.utils.rnn import pad_sequence
|
||||
from ..tokenization_utils import PreTrainedTokenizer
|
||||
|
||||
|
||||
class DataCollator(ABC):
|
||||
"""
|
||||
A `DataCollator` is responsible for batching
|
||||
and pre-processing samples of data as requested by the training loop.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def collate_batch(self) -> Dict[str, torch.Tensor]:
|
||||
"""
|
||||
Take a list of samples from a Dataset and collate them into a batch.
|
||||
|
||||
Returns:
|
||||
A dictionary of tensors
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
InputDataClass = NewType("InputDataClass", Any)
|
||||
|
||||
"""
|
||||
A DataCollator is a function that takes a list of samples from a Dataset
|
||||
and collate them into a batch, as a dictionary of Tensors.
|
||||
"""
|
||||
DataCollator = NewType("DataCollator", Callable[[List[InputDataClass]], Dict[str, torch.Tensor]])
|
||||
|
||||
@dataclass
|
||||
class DefaultDataCollator(DataCollator):
|
||||
|
||||
def default_data_collator(features: List[InputDataClass]) -> Dict[str, torch.Tensor]:
|
||||
"""
|
||||
Very simple data collator that:
|
||||
- simply collates batches of dict-like objects
|
||||
@@ -42,41 +29,43 @@ class DefaultDataCollator(DataCollator):
|
||||
See glue and ner for example of how it's useful.
|
||||
"""
|
||||
|
||||
def collate_batch(self, features: List[InputDataClass]) -> Dict[str, torch.Tensor]:
|
||||
# In this method we'll make the assumption that all `features` in the batch
|
||||
# have the same attributes.
|
||||
# So we will look at the first element as a proxy for what attributes exist
|
||||
# on the whole batch.
|
||||
first = features[0]
|
||||
# In this function we'll make the assumption that all `features` in the batch
|
||||
# have the same attributes.
|
||||
# So we will look at the first element as a proxy for what attributes exist
|
||||
# on the whole batch.
|
||||
if not isinstance(features[0], dict):
|
||||
features = [vars(f) for f in features]
|
||||
|
||||
# Special handling for labels.
|
||||
# Ensure that tensor is created with the correct type
|
||||
# (it should be automatically the case, but let's make sure of it.)
|
||||
if hasattr(first, "label") and first.label is not None:
|
||||
if type(first.label) is int:
|
||||
labels = torch.tensor([f.label for f in features], dtype=torch.long)
|
||||
else:
|
||||
labels = torch.tensor([f.label for f in features], dtype=torch.float)
|
||||
batch = {"labels": labels}
|
||||
elif hasattr(first, "label_ids") and first.label_ids is not None:
|
||||
if type(first.label_ids[0]) is int:
|
||||
labels = torch.tensor([f.label_ids for f in features], dtype=torch.long)
|
||||
else:
|
||||
labels = torch.tensor([f.label_ids for f in features], dtype=torch.float)
|
||||
batch = {"labels": labels}
|
||||
first = features[0]
|
||||
batch = {}
|
||||
|
||||
# Special handling for labels.
|
||||
# Ensure that tensor is created with the correct type
|
||||
# (it should be automatically the case, but let's make sure of it.)
|
||||
if "label" in first:
|
||||
dtype = torch.long if type(first["label"]) is int else torch.float
|
||||
batch["labels"] = torch.tensor([f["label"] for f in features], dtype=dtype)
|
||||
elif "label_ids" in first:
|
||||
if isinstance(first["label_ids"], torch.Tensor):
|
||||
batch["labels"] = torch.stack([f["label_ids"] for f in features])
|
||||
else:
|
||||
batch = {}
|
||||
dtype = torch.long if type(first["label_ids"][0]) is int else torch.float
|
||||
batch["labels"] = torch.tensor([f["label_ids"] for f in features], dtype=dtype)
|
||||
|
||||
# Handling of all other possible attributes.
|
||||
# Again, we will use the first element to figure out which key/values are not None for this model.
|
||||
for k, v in vars(first).items():
|
||||
if k not in ("label", "label_ids") and v is not None and not isinstance(v, str):
|
||||
batch[k] = torch.tensor([getattr(f, k) for f in features], dtype=torch.long)
|
||||
return batch
|
||||
# Handling of all other possible keys.
|
||||
# Again, we will use the first element to figure out which key/values are not None for this model.
|
||||
for k, v in first.items():
|
||||
if k not in ("label", "label_ids") and v is not None and not isinstance(v, str):
|
||||
if isinstance(v, torch.Tensor):
|
||||
batch[k] = torch.stack([f[k] for f in features])
|
||||
else:
|
||||
batch[k] = torch.tensor([f[k] for f in features], dtype=torch.long)
|
||||
|
||||
return batch
|
||||
|
||||
|
||||
@dataclass
|
||||
class DataCollatorForLanguageModeling(DataCollator):
|
||||
class DataCollatorForLanguageModeling:
|
||||
"""
|
||||
Data collator used for language modeling.
|
||||
- collates batches of tensors, honoring their tokenizer's pad_token
|
||||
@@ -87,7 +76,7 @@ class DataCollatorForLanguageModeling(DataCollator):
|
||||
mlm: bool = True
|
||||
mlm_probability: float = 0.15
|
||||
|
||||
def collate_batch(self, examples: List[torch.Tensor]) -> Dict[str, torch.Tensor]:
|
||||
def __call__(self, examples: List[torch.Tensor]) -> Dict[str, torch.Tensor]:
|
||||
batch = self._tensorize_batch(examples)
|
||||
if self.mlm:
|
||||
inputs, labels = self.mask_tokens(batch)
|
||||
|
||||
@@ -83,7 +83,8 @@ class DataProcessor:
|
||||
"""Base class for data converters for sequence classification data sets."""
|
||||
|
||||
def get_example_from_tensor_dict(self, tensor_dict):
|
||||
"""Gets an example from a dict with tensorflow tensors
|
||||
"""Gets an example from a dict with tensorflow tensors.
|
||||
|
||||
Args:
|
||||
tensor_dict: Keys and values should match the corresponding Glue
|
||||
tensorflow_dataset examples.
|
||||
@@ -91,15 +92,15 @@ class DataProcessor:
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_train_examples(self, data_dir):
|
||||
"""Gets a collection of `InputExample`s for the train set."""
|
||||
"""Gets a collection of :class:`InputExample` for the train set."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_dev_examples(self, data_dir):
|
||||
"""Gets a collection of `InputExample`s for the dev set."""
|
||||
"""Gets a collection of :class:`InputExample` for the dev set."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_test_examples(self, data_dir):
|
||||
"""Gets a collection of `InputExample`s for the test set."""
|
||||
"""Gets a collection of :class:`InputExample` for the test set."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_labels(self):
|
||||
|
||||
@@ -71,9 +71,7 @@ except ImportError:
|
||||
|
||||
|
||||
try:
|
||||
import torch_xla.core.xla_model as xm
|
||||
|
||||
tpu_device = xm.xla_device()
|
||||
import torch_xla.core.xla_model as xm # noqa: F401
|
||||
|
||||
if _torch_available:
|
||||
_torch_tpu_available = True # pylint: disable=
|
||||
@@ -83,6 +81,31 @@ except ImportError:
|
||||
_torch_tpu_available = False
|
||||
|
||||
|
||||
try:
|
||||
import psutil # noqa: F401
|
||||
|
||||
_psutil_available = True
|
||||
|
||||
except ImportError:
|
||||
_psutil_available = False
|
||||
|
||||
|
||||
try:
|
||||
import py3nvml # noqa: F401
|
||||
|
||||
_py3nvml_available = True
|
||||
|
||||
except ImportError:
|
||||
_py3nvml_available = False
|
||||
|
||||
|
||||
try:
|
||||
from apex import amp # noqa: F401
|
||||
|
||||
_has_apex = True
|
||||
except ImportError:
|
||||
_has_apex = False
|
||||
|
||||
default_cache_path = os.path.join(torch_cache_home, "transformers")
|
||||
|
||||
|
||||
@@ -117,6 +140,18 @@ def is_torch_tpu_available():
|
||||
return _torch_tpu_available
|
||||
|
||||
|
||||
def is_psutil_available():
|
||||
return _psutil_available
|
||||
|
||||
|
||||
def is_py3nvml_available():
|
||||
return _py3nvml_available
|
||||
|
||||
|
||||
def is_apex_available():
|
||||
return _has_apex
|
||||
|
||||
|
||||
def add_start_docstrings(*docstr):
|
||||
def docstring_decorator(fn):
|
||||
fn.__doc__ = "".join(docstr) + (fn.__doc__ if fn.__doc__ is not None else "")
|
||||
|
||||
@@ -423,6 +423,8 @@ ALBERT_INPUTS_DOCSTRING = r"""
|
||||
Optionally, instead of passing :obj:`input_ids` you can choose to directly pass an embedded representation.
|
||||
This is useful if you want more control over how to convert `input_ids` indices into associated vectors
|
||||
than the model's internal embedding lookup matrix.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
@@ -507,7 +509,7 @@ class AlbertModel(AlbertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -631,7 +633,7 @@ class AlbertForPreTraining(AlbertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -778,7 +780,7 @@ class AlbertForMaskedLM(AlbertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -873,7 +875,7 @@ class AlbertForSequenceClassification(AlbertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -968,7 +970,7 @@ class AlbertForTokenClassification(AlbertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -1074,7 +1076,7 @@ class AlbertForQuestionAnswering(AlbertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -1133,3 +1135,112 @@ class AlbertForQuestionAnswering(AlbertPreTrainedModel):
|
||||
outputs = (total_loss,) + outputs
|
||||
|
||||
return outputs # (loss), start_logits, end_logits, (hidden_states), (attentions)
|
||||
|
||||
|
||||
@add_start_docstrings(
|
||||
"""Albert Model with a multiple choice classification head on top (a linear layer on top of
|
||||
the pooled output and a softmax) e.g. for RocStories/SWAG tasks. """,
|
||||
ALBERT_START_DOCSTRING,
|
||||
)
|
||||
class AlbertForMultipleChoice(AlbertPreTrainedModel):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
|
||||
self.albert = AlbertModel(config)
|
||||
self.dropout = nn.Dropout(config.hidden_dropout_prob)
|
||||
self.classifier = nn.Linear(config.hidden_size, 1)
|
||||
|
||||
self.init_weights()
|
||||
|
||||
@add_start_docstrings_to_callable(ALBERT_INPUTS_DOCSTRING.format("(batch_size, num_choices, sequence_length)"))
|
||||
def forward(
|
||||
self,
|
||||
input_ids=None,
|
||||
attention_mask=None,
|
||||
token_type_ids=None,
|
||||
position_ids=None,
|
||||
head_mask=None,
|
||||
inputs_embeds=None,
|
||||
labels=None,
|
||||
output_attentions=None,
|
||||
):
|
||||
r"""
|
||||
labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size,)`, `optional`, defaults to :obj:`None`):
|
||||
Labels for computing the multiple choice classification loss.
|
||||
Indices should be in ``[0, ..., num_choices-1]`` where `num_choices` is the size of the second dimension
|
||||
of the input tensors. (see `input_ids` above)
|
||||
|
||||
Returns:
|
||||
:obj:`tuple(torch.FloatTensor)` comprising various elements depending on the configuration (:class:`~transformers.BertConfig`) and inputs:
|
||||
loss (:obj:`torch.FloatTensor` of shape `(1,)`, `optional`, returned when :obj:`labels` is provided):
|
||||
Classification loss.
|
||||
classification_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, num_choices)`):
|
||||
`num_choices` is the second dimension of the input tensors. (see `input_ids` above).
|
||||
|
||||
Classification scores (before SoftMax).
|
||||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``config.output_hidden_states=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for the output of the embeddings + one for the output of each layer)
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
||||
heads.
|
||||
|
||||
Examples::
|
||||
|
||||
from transformers import AlbertTokenizer, AlbertForMultipleChoice
|
||||
import torch
|
||||
|
||||
tokenizer = AlbertTokenizer.from_pretrained('albert-base-v2')
|
||||
model = AlbertForMultipleChoice.from_pretrained('albert-base-v2')
|
||||
|
||||
prompt = "In Italy, pizza served in formal settings, such as at a restaurant, is presented unsliced."
|
||||
choice0 = "It is eaten with a fork and a knife."
|
||||
choice1 = "It is eaten while held in the hand."
|
||||
labels = torch.tensor(0).unsqueeze(0) # choice0 is correct (according to Wikipedia ;)), batch size 1
|
||||
|
||||
encoding = tokenizer.batch_encode_plus([[prompt, choice0], [prompt, choice1]], return_tensors='pt', pad_to_max_length=True)
|
||||
outputs = model(**{k: v.unsqueeze(0) for k,v in encoding.items()}, labels=labels) # batch size is 1
|
||||
|
||||
# the linear classifier still needs to be trained
|
||||
loss, logits = outputs[:2]
|
||||
"""
|
||||
num_choices = input_ids.shape[1] if input_ids is not None else inputs_embeds.shape[1]
|
||||
|
||||
input_ids = input_ids.view(-1, input_ids.size(-1)) if input_ids is not None else None
|
||||
attention_mask = attention_mask.view(-1, attention_mask.size(-1)) if attention_mask is not None else None
|
||||
token_type_ids = token_type_ids.view(-1, token_type_ids.size(-1)) if token_type_ids is not None else None
|
||||
position_ids = position_ids.view(-1, position_ids.size(-1)) if position_ids is not None else None
|
||||
inputs_embeds = (
|
||||
inputs_embeds.view(-1, inputs_embeds.size(-2), inputs_embeds.size(-1))
|
||||
if inputs_embeds is not None
|
||||
else None
|
||||
)
|
||||
outputs = self.albert(
|
||||
input_ids,
|
||||
attention_mask=attention_mask,
|
||||
token_type_ids=token_type_ids,
|
||||
position_ids=position_ids,
|
||||
head_mask=head_mask,
|
||||
inputs_embeds=inputs_embeds,
|
||||
output_attentions=output_attentions,
|
||||
)
|
||||
|
||||
pooled_output = outputs[1]
|
||||
|
||||
pooled_output = self.dropout(pooled_output)
|
||||
logits = self.classifier(pooled_output)
|
||||
reshaped_logits = logits.view(-1, num_choices)
|
||||
|
||||
outputs = (reshaped_logits,) + outputs[2:] # add hidden states and attention if they are here
|
||||
|
||||
if labels is not None:
|
||||
loss_fct = CrossEntropyLoss()
|
||||
loss = loss_fct(reshaped_logits, labels)
|
||||
outputs = (loss,) + outputs
|
||||
|
||||
return outputs # (loss), reshaped_logits, (hidden_states), (attentions)
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
|
||||
import logging
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
|
||||
from .configuration_auto import (
|
||||
@@ -33,6 +34,7 @@ from .configuration_auto import (
|
||||
LongformerConfig,
|
||||
OpenAIGPTConfig,
|
||||
ReformerConfig,
|
||||
RetriBertConfig,
|
||||
RobertaConfig,
|
||||
T5Config,
|
||||
TransfoXLConfig,
|
||||
@@ -44,13 +46,19 @@ from .configuration_marian import MarianConfig
|
||||
from .configuration_utils import PretrainedConfig
|
||||
from .modeling_albert import (
|
||||
AlbertForMaskedLM,
|
||||
AlbertForMultipleChoice,
|
||||
AlbertForPreTraining,
|
||||
AlbertForQuestionAnswering,
|
||||
AlbertForSequenceClassification,
|
||||
AlbertForTokenClassification,
|
||||
AlbertModel,
|
||||
)
|
||||
from .modeling_bart import BartForConditionalGeneration, BartForSequenceClassification, BartModel
|
||||
from .modeling_bart import (
|
||||
BartForConditionalGeneration,
|
||||
BartForQuestionAnswering,
|
||||
BartForSequenceClassification,
|
||||
BartModel,
|
||||
)
|
||||
from .modeling_bert import (
|
||||
BertForMaskedLM,
|
||||
BertForMultipleChoice,
|
||||
@@ -58,6 +66,7 @@ from .modeling_bert import (
|
||||
BertForQuestionAnswering,
|
||||
BertForSequenceClassification,
|
||||
BertForTokenClassification,
|
||||
BertLMHeadModel,
|
||||
BertModel,
|
||||
)
|
||||
from .modeling_camembert import (
|
||||
@@ -70,6 +79,7 @@ from .modeling_camembert import (
|
||||
from .modeling_ctrl import CTRLLMHeadModel, CTRLModel
|
||||
from .modeling_distilbert import (
|
||||
DistilBertForMaskedLM,
|
||||
DistilBertForMultipleChoice,
|
||||
DistilBertForQuestionAnswering,
|
||||
DistilBertForSequenceClassification,
|
||||
DistilBertForTokenClassification,
|
||||
@@ -78,6 +88,7 @@ from .modeling_distilbert import (
|
||||
from .modeling_electra import (
|
||||
ElectraForMaskedLM,
|
||||
ElectraForPreTraining,
|
||||
ElectraForQuestionAnswering,
|
||||
ElectraForSequenceClassification,
|
||||
ElectraForTokenClassification,
|
||||
ElectraModel,
|
||||
@@ -101,6 +112,7 @@ from .modeling_longformer import (
|
||||
from .modeling_marian import MarianMTModel
|
||||
from .modeling_openai import OpenAIGPTLMHeadModel, OpenAIGPTModel
|
||||
from .modeling_reformer import ReformerModel, ReformerModelWithLMHead
|
||||
from .modeling_retribert import RetriBertModel
|
||||
from .modeling_roberta import (
|
||||
RobertaForMaskedLM,
|
||||
RobertaForMultipleChoice,
|
||||
@@ -141,6 +153,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
MODEL_MAPPING = OrderedDict(
|
||||
[
|
||||
(RetriBertConfig, RetriBertModel),
|
||||
(T5Config, T5Model),
|
||||
(DistilBertConfig, DistilBertModel),
|
||||
(AlbertConfig, AlbertModel),
|
||||
@@ -164,6 +177,7 @@ MODEL_MAPPING = OrderedDict(
|
||||
|
||||
MODEL_FOR_PRETRAINING_MAPPING = OrderedDict(
|
||||
[
|
||||
(RetriBertConfig, RetriBertModel),
|
||||
(T5Config, T5ForConditionalGeneration),
|
||||
(DistilBertConfig, DistilBertForMaskedLM),
|
||||
(AlbertConfig, AlbertForPreTraining),
|
||||
@@ -209,6 +223,46 @@ MODEL_WITH_LM_HEAD_MAPPING = OrderedDict(
|
||||
]
|
||||
)
|
||||
|
||||
MODEL_FOR_CAUSAL_LM_MAPPING = OrderedDict(
|
||||
[
|
||||
(BertConfig, BertLMHeadModel),
|
||||
(OpenAIGPTConfig, OpenAIGPTLMHeadModel),
|
||||
(GPT2Config, GPT2LMHeadModel),
|
||||
(TransfoXLConfig, TransfoXLLMHeadModel),
|
||||
(XLNetConfig, XLNetLMHeadModel),
|
||||
(
|
||||
XLMConfig,
|
||||
XLMWithLMHeadModel,
|
||||
), # XLM can be MLM and CLM => model should be split similar to BERT; leave here for now
|
||||
(CTRLConfig, CTRLLMHeadModel),
|
||||
(ReformerConfig, ReformerModelWithLMHead),
|
||||
]
|
||||
)
|
||||
|
||||
MODEL_FOR_MASKED_LM_MAPPING = OrderedDict(
|
||||
[
|
||||
(DistilBertConfig, DistilBertForMaskedLM),
|
||||
(AlbertConfig, AlbertForMaskedLM),
|
||||
(CamembertConfig, CamembertForMaskedLM),
|
||||
(XLMRobertaConfig, XLMRobertaForMaskedLM),
|
||||
(LongformerConfig, LongformerForMaskedLM),
|
||||
(RobertaConfig, RobertaForMaskedLM),
|
||||
(BertConfig, BertForMaskedLM),
|
||||
(FlaubertConfig, FlaubertWithLMHeadModel),
|
||||
(XLMConfig, XLMWithLMHeadModel),
|
||||
(ElectraConfig, ElectraForMaskedLM),
|
||||
]
|
||||
)
|
||||
|
||||
MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING = OrderedDict(
|
||||
[
|
||||
(T5Config, T5ForConditionalGeneration),
|
||||
(MarianConfig, MarianMTModel),
|
||||
(BartConfig, BartForConditionalGeneration),
|
||||
(EncoderDecoderConfig, EncoderDecoderModel),
|
||||
]
|
||||
)
|
||||
|
||||
MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING = OrderedDict(
|
||||
[
|
||||
(DistilBertConfig, DistilBertForSequenceClassification),
|
||||
@@ -230,6 +284,7 @@ MODEL_FOR_QUESTION_ANSWERING_MAPPING = OrderedDict(
|
||||
[
|
||||
(DistilBertConfig, DistilBertForQuestionAnswering),
|
||||
(AlbertConfig, AlbertForQuestionAnswering),
|
||||
(BartConfig, BartForQuestionAnswering),
|
||||
(LongformerConfig, LongformerForQuestionAnswering),
|
||||
(XLMRobertaConfig, XLMRobertaForQuestionAnswering),
|
||||
(RobertaConfig, RobertaForQuestionAnswering),
|
||||
@@ -237,6 +292,7 @@ MODEL_FOR_QUESTION_ANSWERING_MAPPING = OrderedDict(
|
||||
(XLNetConfig, XLNetForQuestionAnsweringSimple),
|
||||
(FlaubertConfig, FlaubertForQuestionAnsweringSimple),
|
||||
(XLMConfig, XLMForQuestionAnsweringSimple),
|
||||
(ElectraConfig, ElectraForQuestionAnswering),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -263,7 +319,9 @@ MODEL_FOR_MULTIPLE_CHOICE_MAPPING = OrderedDict(
|
||||
(LongformerConfig, LongformerForMultipleChoice),
|
||||
(RobertaConfig, RobertaForMultipleChoice),
|
||||
(BertConfig, BertForMultipleChoice),
|
||||
(DistilBertConfig, DistilBertForMultipleChoice),
|
||||
(XLNetConfig, XLNetForMultipleChoice),
|
||||
(AlbertConfig, AlbertForMultipleChoice),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -335,6 +393,7 @@ class AutoModel:
|
||||
The `from_pretrained()` method takes care of returning the correct model class instance
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
|
||||
- `t5`: :class:`~transformers.T5Model` (T5 model)
|
||||
- `distilbert`: :class:`~transformers.DistilBertModel` (DistilBERT model)
|
||||
- `albert`: :class:`~transformers.AlbertModel` (ALBERT model)
|
||||
@@ -488,6 +547,7 @@ class AutoModelForPreTraining:
|
||||
The `from_pretrained()` method takes care of returning the correct model class instance
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
|
||||
- `t5`: :class:`~transformers.T5ModelWithLMHead` (T5 model)
|
||||
- `distilbert`: :class:`~transformers.DistilBertForMaskedLM` (DistilBERT model)
|
||||
- `albert`: :class:`~transformers.AlbertForMaskedLM` (ALBERT model)
|
||||
@@ -618,6 +678,10 @@ class AutoModelWithLMHead:
|
||||
config = BertConfig.from_pretrained('bert-base-uncased') # Download configuration from S3 and cache.
|
||||
model = AutoModelWithLMHead.from_config(config) # E.g. model was saved using `save_pretrained('./test/saved_model/')`
|
||||
"""
|
||||
warnings.warn(
|
||||
"The class `AutoModelWithLMHead` is deprecated and will be removed in a future version. Please use `AutoModelForCausalLM` for causal language models, `AutoModelForMaskedLM` for masked language models and `AutoModelForSeq2SeqLM` for encoder-decoder models.",
|
||||
FutureWarning,
|
||||
)
|
||||
for config_class, model_class in MODEL_WITH_LM_HEAD_MAPPING.items():
|
||||
if isinstance(config, config_class):
|
||||
return model_class(config)
|
||||
@@ -636,7 +700,8 @@ class AutoModelWithLMHead:
|
||||
The `from_pretrained()` method takes care of returning the correct model class instance
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
- `t5`: :class:`~transformers.T5ModelWithLMHead` (T5 model)
|
||||
|
||||
- `t5`: :class:`~transformers.T5ForConditionalGeneration` (T5 model)
|
||||
- `distilbert`: :class:`~transformers.DistilBertForMaskedLM` (DistilBERT model)
|
||||
- `albert`: :class:`~transformers.AlbertForMaskedLM` (ALBERT model)
|
||||
- `camembert`: :class:`~transformers.CamembertForMaskedLM` (CamemBERT model)
|
||||
@@ -702,6 +767,10 @@ class AutoModelWithLMHead:
|
||||
model = AutoModelWithLMHead.from_pretrained('./tf_model/bert_tf_checkpoint.ckpt.index', from_tf=True, config=config)
|
||||
|
||||
"""
|
||||
warnings.warn(
|
||||
"The class `AutoModelWithLMHead` is deprecated and will be removed in a future version. Please use `AutoModelForCausalLM` for causal language models, `AutoModelForMaskedLM` for masked language models and `AutoModelForSeq2SeqLM` for encoder-decoder models.",
|
||||
FutureWarning,
|
||||
)
|
||||
config = kwargs.pop("config", None)
|
||||
if not isinstance(config, PretrainedConfig):
|
||||
config = AutoConfig.from_pretrained(pretrained_model_name_or_path, **kwargs)
|
||||
@@ -717,6 +786,415 @@ class AutoModelWithLMHead:
|
||||
)
|
||||
|
||||
|
||||
class AutoModelForCausalLM:
|
||||
r"""
|
||||
:class:`~transformers.AutoModelForCausalLM` is a generic model class
|
||||
that will be instantiated as one of the language modeling model classes of the library
|
||||
when created with the `AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path)`
|
||||
class method.
|
||||
|
||||
This class cannot be instantiated using `__init__()` (throws an error).
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
raise EnvironmentError(
|
||||
"AutoModelForCausalLM is designed to be instantiated "
|
||||
"using the `AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path)` or "
|
||||
"`AutoModelForCausalLM.from_config(config)` methods."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config):
|
||||
r""" Instantiates one of the base model classes of the library
|
||||
from a configuration.
|
||||
|
||||
Note:
|
||||
Loading a model from its configuration file does **not** load the model weights.
|
||||
It only affects the model's configuration. Use :func:`~transformers.AutoModel.from_pretrained` to load
|
||||
the model weights
|
||||
|
||||
Args:
|
||||
config (:class:`~transformers.PretrainedConfig`):
|
||||
The model class to instantiate is selected based on the configuration class:
|
||||
|
||||
- isInstance of `bert` configuration class: :class:`~transformers.BertLMHeadModel` (Bert model)
|
||||
- isInstance of `openai-gpt` configuration class: :class:`~transformers.OpenAIGPTLMHeadModel` (OpenAI GPT model)
|
||||
- isInstance of `gpt2` configuration class: :class:`~transformers.GPT2LMHeadModel` (OpenAI GPT-2 model)
|
||||
- isInstance of `ctrl` configuration class: :class:`~transformers.CTRLLMHeadModel` (Salesforce CTRL model)
|
||||
- isInstance of `transfo-xl` configuration class: :class:`~transformers.TransfoXLLMHeadModel` (Transformer-XL model)
|
||||
- isInstance of `xlnet` configuration class: :class:`~transformers.XLNetLMHeadModel` (XLNet model)
|
||||
- isInstance of `reformer` configuration class: :class:`~transformers.ReformerModelWithLMHead` (Reformer model)
|
||||
|
||||
Examples::
|
||||
|
||||
config = GPT2Config.from_pretrained('gpt2') # Download configuration from S3 and cache.
|
||||
model = AutoModelForCausalLM.from_config(config) # E.g. model was saved using `save_pretrained('./test/saved_model/')`
|
||||
"""
|
||||
for config_class, model_class in MODEL_FOR_CAUSAL_LM_MAPPING.items():
|
||||
if isinstance(config, config_class):
|
||||
return model_class(config)
|
||||
raise ValueError(
|
||||
"Unrecognized configuration class {} for this kind of AutoModel: {}.\n"
|
||||
"Model type should be one of {}.".format(
|
||||
config.__class__, cls.__name__, ", ".join(c.__name__ for c in MODEL_FOR_CAUSAL_LM_MAPPING.keys())
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs):
|
||||
r""" Instantiates one of the language modeling model classes of the library
|
||||
from a pre-trained model configuration.
|
||||
|
||||
The `from_pretrained()` method takes care of returning the correct model class instance
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
|
||||
- `bert`: :class:`~transformers.BertLMHeadModel` (Bert model)
|
||||
- `openai-gpt`: :class:`~transformers.OpenAIGPTLMHeadModel` (OpenAI GPT model)
|
||||
- `gpt2`: :class:`~transformers.GPT2LMHeadModel` (OpenAI GPT-2 model)
|
||||
- `transfo-xl`: :class:`~transformers.TransfoXLLMHeadModel` (Transformer-XL model)
|
||||
- `xlnet`: :class:`~transformers.XLNetLMHeadModel` (XLNet model)
|
||||
- `ctrl`: :class:`~transformers.CTRLLMHeadModel` (Salesforce CTRL model)
|
||||
- `reformer`: :class:`~transformers.ReformerModelWithLMHead` (Google Reformer model)
|
||||
|
||||
The model is set in evaluation mode by default using `model.eval()` (Dropout modules are deactivated)
|
||||
To train the model, you should first set it back in training mode with `model.train()`
|
||||
|
||||
Args:
|
||||
pretrained_model_name_or_path:
|
||||
Either:
|
||||
|
||||
- a string with the `shortcut name` of a pre-trained model to load from cache or download, e.g.: ``bert-base-uncased``.
|
||||
- a string with the `identifier name` of a pre-trained model that was user-uploaded to our S3, e.g.: ``dbmdz/bert-base-german-cased``.
|
||||
- a path to a `directory` containing model weights saved using :func:`~transformers.PreTrainedModel.save_pretrained`, e.g.: ``./my_model_directory/``.
|
||||
- a path or url to a `tensorflow index checkpoint file` (e.g. `./tf_model/model.ckpt.index`). In this case, ``from_tf`` should be set to True and a configuration object should be provided as ``config`` argument. This loading path is slower than converting the TensorFlow checkpoint in a PyTorch model using the provided conversion scripts and loading the PyTorch model afterwards.
|
||||
model_args: (`optional`) Sequence of positional arguments:
|
||||
All remaning positional arguments will be passed to the underlying model's ``__init__`` method
|
||||
config: (`optional`) instance of a class derived from :class:`~transformers.PretrainedConfig`:
|
||||
Configuration for the model to use instead of an automatically loaded configuation. Configuration can be automatically loaded when:
|
||||
|
||||
- the model is a model provided by the library (loaded with the ``shortcut-name`` string of a pretrained model), or
|
||||
- the model was saved using :func:`~transformers.PreTrainedModel.save_pretrained` and is reloaded by suppling the save directory.
|
||||
- the model is loaded by suppling a local directory as ``pretrained_model_name_or_path`` and a configuration JSON file named `config.json` is found in the directory.
|
||||
|
||||
state_dict: (`optional`) dict:
|
||||
an optional state dictionary for the model to use instead of a state dictionary loaded from saved weights file.
|
||||
This option can be used if you want to create a model from a pretrained configuration but load your own weights.
|
||||
In this case though, you should check if using :func:`~transformers.PreTrainedModel.save_pretrained` and :func:`~transformers.PreTrainedModel.from_pretrained` is not a simpler option.
|
||||
cache_dir: (`optional`) string:
|
||||
Path to a directory in which a downloaded pre-trained model
|
||||
configuration should be cached if the standard cache should not be used.
|
||||
force_download: (`optional`) boolean, default False:
|
||||
Force to (re-)download the model weights and configuration files and override the cached versions if they exists.
|
||||
resume_download: (`optional`) boolean, default False:
|
||||
Do not delete incompletely received file. Attempt to resume the download if such a file exists.
|
||||
proxies: (`optional`) dict, default None:
|
||||
A dictionary of proxy servers to use by protocol or endpoint, e.g.: {'http': 'foo.bar:3128', 'http://hostname': 'foo.bar:4012'}.
|
||||
The proxies are used on each request.
|
||||
output_loading_info: (`optional`) boolean:
|
||||
Set to ``True`` to also return a dictionary containing missing keys, unexpected keys and error messages.
|
||||
kwargs: (`optional`) Remaining dictionary of keyword arguments:
|
||||
These arguments will be passed to the configuration and the model.
|
||||
|
||||
Examples::
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained('gpt2') # Download model and configuration from S3 and cache.
|
||||
model = AutoModelForCausalLM.from_pretrained('./test/gpt2_model/') # E.g. model was saved using `save_pretrained('./test/saved_model/')`
|
||||
assert model.config.output_attention == True
|
||||
# Loading from a TF checkpoint file instead of a PyTorch model (slower)
|
||||
config = AutoConfig.from_json_file('./tf_model/gpt2_tf_model_config.json')
|
||||
model = AutoModelForCausalLM.from_pretrained('./tf_model/gpt2_tf_checkpoint.ckpt.index', from_tf=True, config=config)
|
||||
|
||||
"""
|
||||
config = kwargs.pop("config", None)
|
||||
if not isinstance(config, PretrainedConfig):
|
||||
config = AutoConfig.from_pretrained(pretrained_model_name_or_path, **kwargs)
|
||||
|
||||
for config_class, model_class in MODEL_FOR_CAUSAL_LM_MAPPING.items():
|
||||
if isinstance(config, config_class):
|
||||
return model_class.from_pretrained(pretrained_model_name_or_path, *model_args, config=config, **kwargs)
|
||||
raise ValueError(
|
||||
"Unrecognized configuration class {} for this kind of AutoModel: {}.\n"
|
||||
"Model type should be one of {}.".format(
|
||||
config.__class__, cls.__name__, ", ".join(c.__name__ for c in MODEL_FOR_CAUSAL_LM_MAPPING.keys())
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class AutoModelForMaskedLM:
|
||||
r"""
|
||||
:class:`~transformers.AutoModelForMaskedLM` is a generic model class
|
||||
that will be instantiated as one of the language modeling model classes of the library
|
||||
when created with the `AutoModelForMaskedLM.from_pretrained(pretrained_model_name_or_path)`
|
||||
class method.
|
||||
|
||||
This class cannot be instantiated using `__init__()` (throws an error).
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
raise EnvironmentError(
|
||||
"AutoModelForMaskedLM is designed to be instantiated "
|
||||
"using the `AutoModelForMaskedLM.from_pretrained(pretrained_model_name_or_path)` or "
|
||||
"`AutoModelForMaskedLM.from_config(config)` methods."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config):
|
||||
r""" Instantiates one of the base model classes of the library
|
||||
from a configuration.
|
||||
|
||||
Note:
|
||||
Loading a model from its configuration file does **not** load the model weights.
|
||||
It only affects the model's configuration. Use :func:`~transformers.AutoModel.from_pretrained` to load
|
||||
the model weights
|
||||
|
||||
Args:
|
||||
config (:class:`~transformers.PretrainedConfig`):
|
||||
The model class to instantiate is selected based on the configuration class:
|
||||
- isInstance of `distilbert` configuration class: :class:`~transformers.DistilBertForMaskedLM` (DistilBERT model)
|
||||
- isInstance of `longformer` configuration class: :class:`~transformers.LongformerForMaskedLM` (Longformer model)
|
||||
- isInstance of `roberta` configuration class: :class:`~transformers.RobertaForMaskedLM` (RoBERTa model)
|
||||
- isInstance of `bert` configuration class: :class:`~transformers.BertForMaskedLM` (Bert model)
|
||||
- isInstance of `flaubert` configuration class: :class:`~transformers.FlaubertWithLMHeadModel` (Flaubert model)
|
||||
- isInstance of `xlm` configuration class: :class:`~transformers.XLMWithLMHeadModel` (XLM model)
|
||||
- isInstance of `xlm-roberta` configuration class: :class:`~transformers.XLMRobertaForMaskedLM` (XLM-Roberta model)
|
||||
- isInstance of `electra` configuration class: :class:`~transformers.ElectraForMaskedLM` (Electra model)
|
||||
- isInstance of `camembert` configuration class: :class:`~transformers.CamembertForMaskedLM` (Camembert model)
|
||||
- isInstance of `albert` configuration class: :class:`~transformers.AlbertForMaskedLM` (Albert model)
|
||||
|
||||
|
||||
Examples::
|
||||
|
||||
config = BertConfig.from_pretrained('bert-base-uncased') # Download configuration from S3 and cache.
|
||||
model = AutoModelForMaskedLM.from_config(config) # E.g. model was saved using `save_pretrained('./test/saved_model/')`
|
||||
"""
|
||||
for config_class, model_class in MODEL_FOR_MASKED_LM_MAPPING.items():
|
||||
if isinstance(config, config_class):
|
||||
return model_class(config)
|
||||
raise ValueError(
|
||||
"Unrecognized configuration class {} for this kind of AutoModel: {}.\n"
|
||||
"Model type should be one of {}.".format(
|
||||
config.__class__, cls.__name__, ", ".join(c.__name__ for c in MODEL_FOR_MASKED_LM_MAPPING.keys())
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs):
|
||||
r""" Instantiates one of the language modeling model classes of the library
|
||||
from a pre-trained model configuration.
|
||||
|
||||
The `from_pretrained()` method takes care of returning the correct model class instance
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
|
||||
- `distilbert`: :class:`~transformers.DistilBertForMaskedLM` (DistilBERT model)
|
||||
- `albert`: :class:`~transformers.AlbertForMaskedLM` (ALBERT model)
|
||||
- `camembert`: :class:`~transformers.CamembertForMaskedLM` (CamemBERT model)
|
||||
- `xlm-roberta`: :class:`~transformers.XLMRobertaForMaskedLM` (XLM-RoBERTa model)
|
||||
- `longformer`: :class:`~transformers.LongformerForMaskedLM` (Longformer model)
|
||||
- `roberta`: :class:`~transformers.RobertaForMaskedLM` (RoBERTa model)
|
||||
- `xlm`: :class:`~transformers.XLMWithLMHeadModel` (XLM model)
|
||||
- `flaubert`: :class:`~transformers.FlaubertWithLMHeadModel` (Flaubert model)
|
||||
- `electra`: :class:`~transformers.ElectraForMaskedLM` (Electra model)
|
||||
- `bert`: :class:`~transformers.BertLMHeadModel` (Bert model)
|
||||
|
||||
The model is set in evaluation mode by default using `model.eval()` (Dropout modules are deactivated)
|
||||
To train the model, you should first set it back in training mode with `model.train()`
|
||||
|
||||
Args:
|
||||
pretrained_model_name_or_path:
|
||||
Either:
|
||||
|
||||
- a string with the `shortcut name` of a pre-trained model to load from cache or download, e.g.: ``bert-base-uncased``.
|
||||
- a string with the `identifier name` of a pre-trained model that was user-uploaded to our S3, e.g.: ``dbmdz/bert-base-german-cased``.
|
||||
- a path to a `directory` containing model weights saved using :func:`~transformers.PreTrainedModel.save_pretrained`, e.g.: ``./my_model_directory/``.
|
||||
- a path or url to a `tensorflow index checkpoint file` (e.g. `./tf_model/model.ckpt.index`). In this case, ``from_tf`` should be set to True and a configuration object should be provided as ``config`` argument. This loading path is slower than converting the TensorFlow checkpoint in a PyTorch model using the provided conversion scripts and loading the PyTorch model afterwards.
|
||||
model_args: (`optional`) Sequence of positional arguments:
|
||||
All remaning positional arguments will be passed to the underlying model's ``__init__`` method
|
||||
config: (`optional`) instance of a class derived from :class:`~transformers.PretrainedConfig`:
|
||||
Configuration for the model to use instead of an automatically loaded configuation. Configuration can be automatically loaded when:
|
||||
|
||||
- the model is a model provided by the library (loaded with the ``shortcut-name`` string of a pretrained model), or
|
||||
- the model was saved using :func:`~transformers.PreTrainedModel.save_pretrained` and is reloaded by suppling the save directory.
|
||||
- the model is loaded by suppling a local directory as ``pretrained_model_name_or_path`` and a configuration JSON file named `config.json` is found in the directory.
|
||||
|
||||
state_dict: (`optional`) dict:
|
||||
an optional state dictionary for the model to use instead of a state dictionary loaded from saved weights file.
|
||||
This option can be used if you want to create a model from a pretrained configuration but load your own weights.
|
||||
In this case though, you should check if using :func:`~transformers.PreTrainedModel.save_pretrained` and :func:`~transformers.PreTrainedModel.from_pretrained` is not a simpler option.
|
||||
cache_dir: (`optional`) string:
|
||||
Path to a directory in which a downloaded pre-trained model
|
||||
configuration should be cached if the standard cache should not be used.
|
||||
force_download: (`optional`) boolean, default False:
|
||||
Force to (re-)download the model weights and configuration files and override the cached versions if they exists.
|
||||
resume_download: (`optional`) boolean, default False:
|
||||
Do not delete incompletely received file. Attempt to resume the download if such a file exists.
|
||||
proxies: (`optional`) dict, default None:
|
||||
A dictionary of proxy servers to use by protocol or endpoint, e.g.: {'http': 'foo.bar:3128', 'http://hostname': 'foo.bar:4012'}.
|
||||
The proxies are used on each request.
|
||||
output_loading_info: (`optional`) boolean:
|
||||
Set to ``True`` to also return a dictionary containing missing keys, unexpected keys and error messages.
|
||||
kwargs: (`optional`) Remaining dictionary of keyword arguments:
|
||||
These arguments will be passed to the configuration and the model.
|
||||
|
||||
Examples::
|
||||
|
||||
model = AutoModelForMaskedLM.from_pretrained('bert') # Download model and configuration from S3 and cache.
|
||||
model = AutoModelForMaskedLM.from_pretrained('./test/bert_model/') # E.g. model was saved using `save_pretrained('./test/saved_model/')`
|
||||
assert model.config.output_attention == True
|
||||
# Loading from a TF checkpoint file instead of a PyTorch model (slower)
|
||||
config = AutoConfig.from_json_file('./tf_model/bert_tf_model_config.json')
|
||||
model = AutoModelForMaskedLM.from_pretrained('./tf_model/bert_tf_checkpoint.ckpt.index', from_tf=True, config=config)
|
||||
|
||||
"""
|
||||
config = kwargs.pop("config", None)
|
||||
if not isinstance(config, PretrainedConfig):
|
||||
config = AutoConfig.from_pretrained(pretrained_model_name_or_path, **kwargs)
|
||||
|
||||
for config_class, model_class in MODEL_FOR_MASKED_LM_MAPPING.items():
|
||||
if isinstance(config, config_class):
|
||||
return model_class.from_pretrained(pretrained_model_name_or_path, *model_args, config=config, **kwargs)
|
||||
raise ValueError(
|
||||
"Unrecognized configuration class {} for this kind of AutoModel: {}.\n"
|
||||
"Model type should be one of {}.".format(
|
||||
config.__class__, cls.__name__, ", ".join(c.__name__ for c in MODEL_FOR_MASKED_LM_MAPPING.keys())
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class AutoModelForSeq2SeqLM:
|
||||
r"""
|
||||
:class:`~transformers.AutoModelForSeq2SeqLM` is a generic model class
|
||||
that will be instantiated as one of the language modeling model classes of the library
|
||||
when created with the `AutoModelForSeq2SeqLM.from_pretrained(pretrained_model_name_or_path)`
|
||||
class method.
|
||||
|
||||
This class cannot be instantiated using `__init__()` (throws an error).
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
raise EnvironmentError(
|
||||
"AutoModelForSeq2SeqLM is designed to be instantiated "
|
||||
"using the `AutoModelForSeq2SeqLM.from_pretrained(pretrained_model_name_or_path)` or "
|
||||
"`AutoModelForSeq2SeqLM.from_config(config)` methods."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config):
|
||||
r""" Instantiates one of the base model classes of the library
|
||||
from a configuration.
|
||||
|
||||
Note:
|
||||
Loading a model from its configuration file does **not** load the model weights.
|
||||
It only affects the model's configuration. Use :func:`~transformers.AutoModel.from_pretrained` to load
|
||||
the model weights
|
||||
|
||||
Args:
|
||||
config (:class:`~transformers.PretrainedConfig`):
|
||||
The model class to instantiate is selected based on the configuration class:
|
||||
|
||||
- isInstance of `t5` configuration class: :class:`~transformers.T5ForConditionalGeneration` (T5 model)
|
||||
- isInstance of `bart` configuration class: :class:`~transformers.BartForConditionalGeneration` (Bart model)
|
||||
- isInstance of `marian` configuration class: :class:`~transformers.MarianMTModel` (Marian model)
|
||||
- isInstance of `encoder-decoder` configuration class: :class:`~transformers.EncoderDecoderModel` (Encoder Decoder model)
|
||||
|
||||
Examples::
|
||||
|
||||
config = T5Config.from_pretrained('t5')
|
||||
model = AutoModelForSeq2SeqLM.from_config(config) # E.g. model was saved using `save_pretrained('./test/saved_model/')`
|
||||
"""
|
||||
for config_class, model_class in MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING.items():
|
||||
if isinstance(config, config_class):
|
||||
return model_class(config)
|
||||
raise ValueError(
|
||||
"Unrecognized configuration class {} for this kind of AutoModel: {}.\n"
|
||||
"Model type should be one of {}.".format(
|
||||
config.__class__,
|
||||
cls.__name__,
|
||||
", ".join(c.__name__ for c in MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING.keys()),
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs):
|
||||
r""" Instantiates one of the language modeling model classes of the library
|
||||
from a pre-trained model configuration.
|
||||
|
||||
The `from_pretrained()` method takes care of returning the correct model class instance
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
|
||||
- `t5`: :class:`~transformers.T5ForConditionalGeneration` (T5 model)
|
||||
- `bart`: :class:`~transformers.BartForConditionalGeneration` (Bert model)
|
||||
- `marian`: :class:`~transformers.MarianMTModel` (Marian model)
|
||||
- `encoder-decoder`: :class:`~transformers.EncoderDecoderModel` (Encoder Decoder model)
|
||||
|
||||
The model is set in evaluation mode by default using `model.eval()` (Dropout modules are deactivated)
|
||||
To train the model, you should first set it back in training mode with `model.train()`
|
||||
|
||||
Args:
|
||||
pretrained_model_name_or_path:
|
||||
Either:
|
||||
|
||||
- a string with the `shortcut name` of a pre-trained model to load from cache or download, e.g.: ``bert-base-uncased``.
|
||||
- a string with the `identifier name` of a pre-trained model that was user-uploaded to our S3, e.g.: ``dbmdz/bert-base-german-cased``.
|
||||
- a path to a `directory` containing model weights saved using :func:`~transformers.PreTrainedModel.save_pretrained`, e.g.: ``./my_model_directory/``.
|
||||
- a path or url to a `tensorflow index checkpoint file` (e.g. `./tf_model/model.ckpt.index`). In this case, ``from_tf`` should be set to True and a configuration object should be provided as ``config`` argument. This loading path is slower than converting the TensorFlow checkpoint in a PyTorch model using the provided conversion scripts and loading the PyTorch model afterwards.
|
||||
model_args: (`optional`) Sequence of positional arguments:
|
||||
All remaning positional arguments will be passed to the underlying model's ``__init__`` method
|
||||
config: (`optional`) instance of a class derived from :class:`~transformers.PretrainedConfig`:
|
||||
Configuration for the model to use instead of an automatically loaded configuation. Configuration can be automatically loaded when:
|
||||
|
||||
- the model is a model provided by the library (loaded with the ``shortcut-name`` string of a pretrained model), or
|
||||
- the model was saved using :func:`~transformers.PreTrainedModel.save_pretrained` and is reloaded by suppling the save directory.
|
||||
- the model is loaded by suppling a local directory as ``pretrained_model_name_or_path`` and a configuration JSON file named `config.json` is found in the directory.
|
||||
|
||||
state_dict: (`optional`) dict:
|
||||
an optional state dictionary for the model to use instead of a state dictionary loaded from saved weights file.
|
||||
This option can be used if you want to create a model from a pretrained configuration but load your own weights.
|
||||
In this case though, you should check if using :func:`~transformers.PreTrainedModel.save_pretrained` and :func:`~transformers.PreTrainedModel.from_pretrained` is not a simpler option.
|
||||
cache_dir: (`optional`) string:
|
||||
Path to a directory in which a downloaded pre-trained model
|
||||
configuration should be cached if the standard cache should not be used.
|
||||
force_download: (`optional`) boolean, default False:
|
||||
Force to (re-)download the model weights and configuration files and override the cached versions if they exists.
|
||||
resume_download: (`optional`) boolean, default False:
|
||||
Do not delete incompletely received file. Attempt to resume the download if such a file exists.
|
||||
proxies: (`optional`) dict, default None:
|
||||
A dictionary of proxy servers to use by protocol or endpoint, e.g.: {'http': 'foo.bar:3128', 'http://hostname': 'foo.bar:4012'}.
|
||||
The proxies are used on each request.
|
||||
output_loading_info: (`optional`) boolean:
|
||||
Set to ``True`` to also return a dictionary containing missing keys, unexpected keys and error messages.
|
||||
kwargs: (`optional`) Remaining dictionary of keyword arguments:
|
||||
These arguments will be passed to the configuration and the model.
|
||||
|
||||
Examples::
|
||||
|
||||
model = AutoModelForSeq2SeqLM.from_pretrained('t5-base') # Download model and configuration from S3 and cache.
|
||||
model = AutoModelForSeq2SeqLM.from_pretrained('./test/t5_model/') # E.g. model was saved using `save_pretrained('./test/saved_model/')`
|
||||
assert model.config.output_attention == True
|
||||
# Loading from a TF checkpoint file instead of a PyTorch model (slower)
|
||||
config = AutoConfig.from_json_file('./tf_model/t5_tf_model_config.json')
|
||||
model = AutoModelForSeq2SeqLM.from_pretrained('./tf_model/t5_tf_checkpoint.ckpt.index', from_tf=True, config=config)
|
||||
|
||||
"""
|
||||
config = kwargs.pop("config", None)
|
||||
if not isinstance(config, PretrainedConfig):
|
||||
config = AutoConfig.from_pretrained(pretrained_model_name_or_path, **kwargs)
|
||||
|
||||
for config_class, model_class in MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING.items():
|
||||
if isinstance(config, config_class):
|
||||
return model_class.from_pretrained(pretrained_model_name_or_path, *model_args, config=config, **kwargs)
|
||||
raise ValueError(
|
||||
"Unrecognized configuration class {} for this kind of AutoModel: {}.\n"
|
||||
"Model type should be one of {}.".format(
|
||||
config.__class__,
|
||||
cls.__name__,
|
||||
", ".join(c.__name__ for c in MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING.keys()),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class AutoModelForSequenceClassification:
|
||||
r"""
|
||||
:class:`~transformers.AutoModelForSequenceClassification` is a generic model class
|
||||
@@ -784,6 +1262,7 @@ class AutoModelForSequenceClassification:
|
||||
The `from_pretrained()` method takes care of returning the correct model class instance
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
|
||||
- `distilbert`: :class:`~transformers.DistilBertForSequenceClassification` (DistilBERT model)
|
||||
- `albert`: :class:`~transformers.AlbertForSequenceClassification` (ALBERT model)
|
||||
- `camembert`: :class:`~transformers.CamembertForSequenceClassification` (CamemBERT model)
|
||||
@@ -930,6 +1409,7 @@ class AutoModelForQuestionAnswering:
|
||||
The `from_pretrained()` method takes care of returning the correct model class instance
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
|
||||
- `distilbert`: :class:`~transformers.DistilBertForQuestionAnswering` (DistilBERT model)
|
||||
- `albert`: :class:`~transformers.AlbertForQuestionAnswering` (ALBERT model)
|
||||
- `bert`: :class:`~transformers.BertForQuestionAnswering` (Bert model)
|
||||
@@ -1075,6 +1555,7 @@ class AutoModelForTokenClassification:
|
||||
The `from_pretrained()` method takes care of returning the correct model class instance
|
||||
based on the `model_type` property of the config object, or when it's missing,
|
||||
falling back to using pattern matching on the `pretrained_model_name_or_path` string:
|
||||
|
||||
- `distilbert`: :class:`~transformers.DistilBertForTokenClassification` (DistilBERT model)
|
||||
- `xlm`: :class:`~transformers.XLMForTokenClassification` (XLM model)
|
||||
- `xlm-roberta`: :class:`~transformers.XLMRobertaForTokenClassification` (XLM-RoBERTa?Para model)
|
||||
|
||||
@@ -23,6 +23,7 @@ import numpy as np
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from torch import Tensor, nn
|
||||
from torch.nn import CrossEntropyLoss
|
||||
|
||||
from .activations import ACT2FN
|
||||
from .configuration_bart import BartConfig
|
||||
@@ -58,7 +59,7 @@ BART_GENERATION_EXAMPLE = r"""
|
||||
Examples::
|
||||
|
||||
from transformers import BartTokenizer, BartForConditionalGeneration, BartConfig
|
||||
# see ``examples/summarization/bart/evaluate_cnn.py`` for a longer example
|
||||
# see ``examples/summarization/bart/run_eval.py`` for a longer example
|
||||
model = BartForConditionalGeneration.from_pretrained('bart-large-cnn')
|
||||
tokenizer = BartTokenizer.from_pretrained('bart-large-cnn')
|
||||
ARTICLE_TO_SUMMARIZE = "My friends are cool but they eat too many carbs."
|
||||
@@ -89,6 +90,8 @@ BART_INPUTS_DOCSTRING = r"""
|
||||
Default behavior: generate a tensor that ignores pad tokens in decoder_input_ids. Causal mask will also be used by default.
|
||||
If you want to change padding behavior, you should read :func:`~transformers.modeling_bart._prepare_decoder_inputs` and modify.
|
||||
See diagram 1 in the paper for more info on the default strategy
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
@@ -928,7 +931,7 @@ class BartForConditionalGeneration(PretrainedBartModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -980,11 +983,7 @@ class BartForConditionalGeneration(PretrainedBartModel):
|
||||
def prepare_inputs_for_generation(self, decoder_input_ids, past, attention_mask, use_cache, **kwargs):
|
||||
assert past is not None, "past has to be defined for encoder_outputs"
|
||||
|
||||
# first step, decoder_cached_states are empty
|
||||
if not past[1]:
|
||||
encoder_outputs, decoder_cached_states = past, None
|
||||
else:
|
||||
encoder_outputs, decoder_cached_states = past
|
||||
encoder_outputs, decoder_cached_states = past
|
||||
return {
|
||||
"input_ids": None, # encoder_outputs is defined. input_ids not needed
|
||||
"encoder_outputs": encoder_outputs,
|
||||
@@ -1078,7 +1077,7 @@ class BartForSequenceClassification(PretrainedBartModel):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for the output of the embeddings + one for the output of each layer)
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape :obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
Attentions weights after the attention softmax, used to compute the weighted average in the
|
||||
self-attention
|
||||
@@ -1121,6 +1120,122 @@ class BartForSequenceClassification(PretrainedBartModel):
|
||||
return outputs
|
||||
|
||||
|
||||
@add_start_docstrings(
|
||||
"""BART Model with a span classification head on top for extractive question-answering tasks like SQuAD (a linear layer on top of
|
||||
the hidden-states output to compute `span start logits` and `span end logits`). """,
|
||||
BART_START_DOCSTRING,
|
||||
)
|
||||
class BartForQuestionAnswering(PretrainedBartModel):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
|
||||
config.num_labels = 2
|
||||
self.num_labels = config.num_labels
|
||||
|
||||
self.model = BartModel(config)
|
||||
self.qa_outputs = nn.Linear(config.hidden_size, config.num_labels)
|
||||
|
||||
self.model._init_weights(self.qa_outputs)
|
||||
|
||||
@add_start_docstrings_to_callable(BART_INPUTS_DOCSTRING)
|
||||
def forward(
|
||||
self,
|
||||
input_ids,
|
||||
attention_mask=None,
|
||||
encoder_outputs=None,
|
||||
decoder_input_ids=None,
|
||||
decoder_attention_mask=None,
|
||||
start_positions=None,
|
||||
end_positions=None,
|
||||
output_attentions=None,
|
||||
):
|
||||
r"""
|
||||
start_positions (:obj:`torch.LongTensor` of shape :obj:`(batch_size,)`, `optional`, defaults to :obj:`None`):
|
||||
Labels for position (index) of the start of the labelled span for computing the token classification loss.
|
||||
Positions are clamped to the length of the sequence (`sequence_length`).
|
||||
Position outside of the sequence are not taken into account for computing the loss.
|
||||
end_positions (:obj:`torch.LongTensor` of shape :obj:`(batch_size,)`, `optional`, defaults to :obj:`None`):
|
||||
Labels for position (index) of the end of the labelled span for computing the token classification loss.
|
||||
Positions are clamped to the length of the sequence (`sequence_length`).
|
||||
Position outside of the sequence are not taken into account for computing the loss.
|
||||
|
||||
Returns:
|
||||
:obj:`tuple(torch.FloatTensor)` comprising various elements depending on the configuration (:class:`~transformers.BartConfig`) and inputs:
|
||||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided):
|
||||
Total span extraction loss is the sum of a Cross-Entropy for the start and end positions.
|
||||
start_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length,)`):
|
||||
Span-start scores (before SoftMax).
|
||||
end_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length,)`):
|
||||
Span-end scores (before SoftMax).
|
||||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``config.output_hidden_states=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for the output of the embeddings + one for the output of each layer)
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
||||
heads.
|
||||
|
||||
Examples::
|
||||
|
||||
# The checkpoint bart-large is not fine-tuned for question answering. Please see the
|
||||
# examples/question-answering/run_squad.py example to see how to fine-tune a model to a question answering task.
|
||||
|
||||
from transformers import BartTokenizer, BartForQuestionAnswering
|
||||
import torch
|
||||
|
||||
tokenizer = BartTokenizer.from_pretrained('facebook/bart-large')
|
||||
model = BartForQuestionAnswering.from_pretrained('facebook/bart-large')
|
||||
|
||||
question, text = "Who was Jim Henson?", "Jim Henson was a nice puppet"
|
||||
input_ids = tokenizer.encode(question, text)
|
||||
start_scores, end_scores = model(torch.tensor([input_ids]))
|
||||
|
||||
all_tokens = tokenizer.convert_ids_to_tokens(input_ids)
|
||||
answer = ' '.join(all_tokens[torch.argmax(start_scores) : torch.argmax(end_scores)+1])
|
||||
|
||||
"""
|
||||
|
||||
outputs = self.model(
|
||||
input_ids,
|
||||
attention_mask=attention_mask,
|
||||
decoder_input_ids=decoder_input_ids,
|
||||
decoder_attention_mask=decoder_attention_mask,
|
||||
encoder_outputs=encoder_outputs,
|
||||
output_attentions=output_attentions,
|
||||
)
|
||||
|
||||
sequence_output = outputs[0]
|
||||
|
||||
logits = self.qa_outputs(sequence_output)
|
||||
start_logits, end_logits = logits.split(1, dim=-1)
|
||||
start_logits = start_logits.squeeze(-1)
|
||||
end_logits = end_logits.squeeze(-1)
|
||||
|
||||
outputs = (start_logits, end_logits,) + outputs[1:]
|
||||
if start_positions is not None and end_positions is not None:
|
||||
# If we are on multi-GPU, split add a dimension
|
||||
if len(start_positions.size()) > 1:
|
||||
start_positions = start_positions.squeeze(-1)
|
||||
if len(end_positions.size()) > 1:
|
||||
end_positions = end_positions.squeeze(-1)
|
||||
# sometimes the start/end positions are outside our model inputs, we ignore these terms
|
||||
ignored_index = start_logits.size(1)
|
||||
start_positions.clamp_(0, ignored_index)
|
||||
end_positions.clamp_(0, ignored_index)
|
||||
|
||||
loss_fct = CrossEntropyLoss(ignore_index=ignored_index)
|
||||
start_loss = loss_fct(start_logits, start_positions)
|
||||
end_loss = loss_fct(end_logits, end_positions)
|
||||
total_loss = (start_loss + end_loss) / 2
|
||||
outputs = (total_loss,) + outputs
|
||||
|
||||
return outputs # (loss), start_logits, end_logits, (hidden_states), (attentions)
|
||||
|
||||
|
||||
class SinusoidalPositionalEmbedding(nn.Embedding):
|
||||
"""This module produces sinusoidal positional embeddings of any length."""
|
||||
|
||||
|
||||
@@ -594,6 +594,8 @@ BERT_INPUTS_DOCSTRING = r"""
|
||||
is used in the cross-attention if the model is configured as a decoder.
|
||||
Mask values selected in ``[0, 1]``:
|
||||
``1`` for tokens that are NOT MASKED, ``0`` for MASKED tokens.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
@@ -674,7 +676,7 @@ class BertModel(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -814,7 +816,7 @@ class BertForPreTraining(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -871,11 +873,123 @@ class BertForPreTraining(BertPreTrainedModel):
|
||||
return outputs # (loss), prediction_scores, seq_relationship_score, (hidden_states), (attentions)
|
||||
|
||||
|
||||
# TODO: Split with a different BertWithLMHead to get rid of `lm_labels` here and in encoder_decoder.
|
||||
@add_start_docstrings("""Bert Model with a `language modeling` head on top. """, BERT_START_DOCSTRING)
|
||||
class BertForMaskedLM(BertPreTrainedModel):
|
||||
@add_start_docstrings(
|
||||
"""Bert Model with a `language modeling` head on top for CLM fine-tuning. """, BERT_START_DOCSTRING
|
||||
)
|
||||
class BertLMHeadModel(BertPreTrainedModel):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
assert config.is_decoder, "If you want to use `BertLMHeadModel` as a standalone, add `is_decoder=True`."
|
||||
|
||||
self.bert = BertModel(config)
|
||||
self.cls = BertOnlyMLMHead(config)
|
||||
|
||||
self.init_weights()
|
||||
|
||||
def get_output_embeddings(self):
|
||||
return self.cls.predictions.decoder
|
||||
|
||||
@add_start_docstrings_to_callable(BERT_INPUTS_DOCSTRING.format("(batch_size, sequence_length)"))
|
||||
def forward(
|
||||
self,
|
||||
input_ids=None,
|
||||
attention_mask=None,
|
||||
token_type_ids=None,
|
||||
position_ids=None,
|
||||
head_mask=None,
|
||||
inputs_embeds=None,
|
||||
labels=None,
|
||||
encoder_hidden_states=None,
|
||||
encoder_attention_mask=None,
|
||||
output_attentions=None,
|
||||
**kwargs
|
||||
):
|
||||
r"""
|
||||
labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`, defaults to :obj:`None`):
|
||||
Labels for computing the left-to-right language modeling loss (next word prediction).
|
||||
Indices should be in ``[-100, 0, ..., config.vocab_size]`` (see ``input_ids`` docstring)
|
||||
Tokens with indices set to ``-100`` are ignored (masked), the loss is only computed for the tokens with labels
|
||||
in ``[0, ..., config.vocab_size]``
|
||||
kwargs (:obj:`Dict[str, any]`, optional, defaults to `{}`):
|
||||
Used to hide legacy arguments that have been deprecated.
|
||||
|
||||
Returns:
|
||||
:obj:`tuple(torch.FloatTensor)` comprising various elements depending on the configuration (:class:`~transformers.BertConfig`) and inputs:
|
||||
ltr_lm_loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided):
|
||||
Next token prediction loss.
|
||||
prediction_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, config.vocab_size)`)
|
||||
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
|
||||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``config.output_hidden_states=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for the output of the embeddings + one for the output of each layer)
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
||||
heads.
|
||||
|
||||
Examples::
|
||||
|
||||
from transformers import BertTokenizer, BertLMHeadModel
|
||||
import torch
|
||||
|
||||
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
|
||||
model = BertLMHeadModel.from_pretrained('bert-base-uncased', is_decoder=True)
|
||||
|
||||
input_ids = torch.tensor(tokenizer.encode("Hello, my dog is cute", add_special_tokens=True)).unsqueeze(0) # Batch size 1
|
||||
outputs = model(input_ids, labels=input_ids)
|
||||
|
||||
loss, prediction_scores = outputs[:2]
|
||||
|
||||
"""
|
||||
|
||||
outputs = self.bert(
|
||||
input_ids,
|
||||
attention_mask=attention_mask,
|
||||
token_type_ids=token_type_ids,
|
||||
position_ids=position_ids,
|
||||
head_mask=head_mask,
|
||||
inputs_embeds=inputs_embeds,
|
||||
encoder_hidden_states=encoder_hidden_states,
|
||||
encoder_attention_mask=encoder_attention_mask,
|
||||
output_attentions=output_attentions,
|
||||
)
|
||||
|
||||
sequence_output = outputs[0]
|
||||
prediction_scores = self.cls(sequence_output)
|
||||
|
||||
outputs = (prediction_scores,) + outputs[2:] # Add hidden states and attention if they are here
|
||||
|
||||
if labels is not None:
|
||||
# we are doing next-token prediction; shift prediction scores and input ids by one
|
||||
prediction_scores = prediction_scores[:, :-1, :].contiguous()
|
||||
labels = labels[:, 1:].contiguous()
|
||||
loss_fct = CrossEntropyLoss()
|
||||
ltr_lm_loss = loss_fct(prediction_scores.view(-1, self.config.vocab_size), labels.view(-1))
|
||||
outputs = (ltr_lm_loss,) + outputs
|
||||
|
||||
return outputs # (ltr_lm_loss), prediction_scores, (hidden_states), (attentions)
|
||||
|
||||
def prepare_inputs_for_generation(self, input_ids, attention_mask=None, **model_kwargs):
|
||||
input_shape = input_ids.shape
|
||||
|
||||
# if model is used as a decoder in encoder-decoder model, the decoder attention mask is created on the fly
|
||||
if attention_mask is None:
|
||||
attention_mask = input_ids.new_ones(input_shape)
|
||||
|
||||
return {"input_ids": input_ids, "attention_mask": attention_mask}
|
||||
|
||||
|
||||
@add_start_docstrings("""Bert Model with a `language modeling` head on top. """, BERT_START_DOCSTRING)
|
||||
class BertForMaskedLM(BertPreTrainedModel):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
assert (
|
||||
not config.is_decoder
|
||||
), "If you want to use `BertForMaskedLM` make sure `config.is_decoder=False` for bi-directional self-attention."
|
||||
|
||||
self.bert = BertModel(config)
|
||||
self.cls = BertOnlyMLMHead(config)
|
||||
@@ -897,7 +1011,6 @@ class BertForMaskedLM(BertPreTrainedModel):
|
||||
labels=None,
|
||||
encoder_hidden_states=None,
|
||||
encoder_attention_mask=None,
|
||||
lm_labels=None,
|
||||
output_attentions=None,
|
||||
**kwargs
|
||||
):
|
||||
@@ -907,11 +1020,6 @@ class BertForMaskedLM(BertPreTrainedModel):
|
||||
Indices should be in ``[-100, 0, ..., config.vocab_size]`` (see ``input_ids`` docstring)
|
||||
Tokens with indices set to ``-100`` are ignored (masked), the loss is only computed for the tokens with labels
|
||||
in ``[0, ..., config.vocab_size]``
|
||||
lm_labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`, defaults to :obj:`None`):
|
||||
Labels for computing the left-to-right language modeling loss (next word prediction).
|
||||
Indices should be in ``[-100, 0, ..., config.vocab_size]`` (see ``input_ids`` docstring)
|
||||
Tokens with indices set to ``-100`` are ignored (masked), the loss is only computed for the tokens with labels
|
||||
in ``[0, ..., config.vocab_size]``
|
||||
kwargs (:obj:`Dict[str, any]`, optional, defaults to `{}`):
|
||||
Used to hide legacy arguments that have been deprecated.
|
||||
|
||||
@@ -919,8 +1027,6 @@ class BertForMaskedLM(BertPreTrainedModel):
|
||||
:obj:`tuple(torch.FloatTensor)` comprising various elements depending on the configuration (:class:`~transformers.BertConfig`) and inputs:
|
||||
masked_lm_loss (`optional`, returned when ``labels`` is provided) ``torch.FloatTensor`` of shape ``(1,)``:
|
||||
Masked language modeling loss.
|
||||
ltr_lm_loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`lm_labels` is provided):
|
||||
Next token prediction loss.
|
||||
prediction_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, config.vocab_size)`)
|
||||
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
|
||||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``config.output_hidden_states=True``):
|
||||
@@ -928,7 +1034,7 @@ class BertForMaskedLM(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -955,6 +1061,7 @@ class BertForMaskedLM(BertPreTrainedModel):
|
||||
DeprecationWarning,
|
||||
)
|
||||
labels = kwargs.pop("masked_lm_labels")
|
||||
assert "lm_labels" not in kwargs, "Use `BertWithLMHead` for autoregressive language modeling task."
|
||||
assert kwargs == {}, f"Unexpected keyword arguments: {list(kwargs.keys())}."
|
||||
|
||||
outputs = self.bert(
|
||||
@@ -974,46 +1081,24 @@ class BertForMaskedLM(BertPreTrainedModel):
|
||||
|
||||
outputs = (prediction_scores,) + outputs[2:] # Add hidden states and attention if they are here
|
||||
|
||||
# Although this may seem awkward, BertForMaskedLM supports two scenarios:
|
||||
# 1. If a tensor that contains the indices of masked labels is provided,
|
||||
# the cross-entropy is the MLM cross-entropy that measures the likelihood
|
||||
# of predictions for masked words.
|
||||
# 2. If `lm_labels` is provided we are in a causal scenario where we
|
||||
# try to predict the next token for each input in the decoder.
|
||||
if labels is not None:
|
||||
loss_fct = CrossEntropyLoss() # -100 index = padding token
|
||||
masked_lm_loss = loss_fct(prediction_scores.view(-1, self.config.vocab_size), labels.view(-1))
|
||||
outputs = (masked_lm_loss,) + outputs
|
||||
|
||||
if lm_labels is not None:
|
||||
# we are doing next-token prediction; shift prediction scores and input ids by one
|
||||
prediction_scores = prediction_scores[:, :-1, :].contiguous()
|
||||
lm_labels = lm_labels[:, 1:].contiguous()
|
||||
loss_fct = CrossEntropyLoss()
|
||||
ltr_lm_loss = loss_fct(prediction_scores.view(-1, self.config.vocab_size), lm_labels.view(-1))
|
||||
outputs = (ltr_lm_loss,) + outputs
|
||||
|
||||
return outputs # (ltr_lm_loss), (masked_lm_loss), prediction_scores, (hidden_states), (attentions)
|
||||
return outputs # (masked_lm_loss), prediction_scores, (hidden_states), (attentions)
|
||||
|
||||
def prepare_inputs_for_generation(self, input_ids, attention_mask=None, **model_kwargs):
|
||||
input_shape = input_ids.shape
|
||||
effective_batch_size = input_shape[0]
|
||||
|
||||
# if model is used as a decoder in encoder-decoder model, the decoder attention mask is created on the fly
|
||||
if attention_mask is None:
|
||||
attention_mask = input_ids.new_ones(input_shape)
|
||||
|
||||
# if model is does not use a causal mask then add a dummy token
|
||||
if self.config.is_decoder is False:
|
||||
assert self.config.pad_token_id is not None, "The PAD token should be defined for generation"
|
||||
attention_mask = torch.cat(
|
||||
[attention_mask, attention_mask.new_zeros((attention_mask.shape[0], 1))], dim=-1
|
||||
)
|
||||
|
||||
dummy_token = torch.full(
|
||||
(effective_batch_size, 1), self.config.pad_token_id, dtype=torch.long, device=input_ids.device
|
||||
)
|
||||
input_ids = torch.cat([input_ids, dummy_token], dim=1)
|
||||
# add a dummy token
|
||||
assert self.config.pad_token_id is not None, "The PAD token should be defined for generation"
|
||||
attention_mask = torch.cat([attention_mask, attention_mask.new_zeros((attention_mask.shape[0], 1))], dim=-1)
|
||||
dummy_token = torch.full(
|
||||
(effective_batch_size, 1), self.config.pad_token_id, dtype=torch.long, device=input_ids.device
|
||||
)
|
||||
input_ids = torch.cat([input_ids, dummy_token], dim=1)
|
||||
|
||||
return {"input_ids": input_ids, "attention_mask": attention_mask}
|
||||
|
||||
@@ -1060,7 +1145,7 @@ class BertForNextSentencePrediction(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -1152,7 +1237,7 @@ class BertForSequenceClassification(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -1251,7 +1336,7 @@ class BertForMultipleChoice(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -1269,7 +1354,7 @@ class BertForMultipleChoice(BertPreTrainedModel):
|
||||
prompt = "In Italy, pizza served in formal settings, such as at a restaurant, is presented unsliced."
|
||||
choice0 = "It is eaten with a fork and a knife."
|
||||
choice1 = "It is eaten while held in the hand."
|
||||
labels = torch.tensor(0) # choice0 is correct (according to Wikipedia ;))
|
||||
labels = torch.tensor(0).unsqueeze(0) # choice0 is correct (according to Wikipedia ;)), batch size 1
|
||||
|
||||
encoding = tokenizer.batch_encode_plus([[prompt, choice0], [prompt, choice1]], return_tensors='pt', pad_to_max_length=True)
|
||||
outputs = model(**{k: v.unsqueeze(0) for k,v in encoding.items()}, labels=labels) # batch size is 1
|
||||
@@ -1277,12 +1362,17 @@ class BertForMultipleChoice(BertPreTrainedModel):
|
||||
# the linear classifier still needs to be trained
|
||||
loss, logits = outputs[:2]
|
||||
"""
|
||||
num_choices = input_ids.shape[1]
|
||||
num_choices = input_ids.shape[1] if input_ids is not None else inputs_embeds.shape[1]
|
||||
|
||||
input_ids = input_ids.view(-1, input_ids.size(-1))
|
||||
input_ids = input_ids.view(-1, input_ids.size(-1)) if input_ids is not None else None
|
||||
attention_mask = attention_mask.view(-1, attention_mask.size(-1)) if attention_mask is not None else None
|
||||
token_type_ids = token_type_ids.view(-1, token_type_ids.size(-1)) if token_type_ids is not None else None
|
||||
position_ids = position_ids.view(-1, position_ids.size(-1)) if position_ids is not None else None
|
||||
inputs_embeds = (
|
||||
inputs_embeds.view(-1, inputs_embeds.size(-2), inputs_embeds.size(-1))
|
||||
if inputs_embeds is not None
|
||||
else None
|
||||
)
|
||||
|
||||
outputs = self.bert(
|
||||
input_ids,
|
||||
@@ -1354,7 +1444,7 @@ class BertForTokenClassification(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -1461,7 +1551,7 @@ class BertForQuestionAnswering(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ CAMEMBERT_START_DOCSTRING = r"""
|
||||
model. Initializing with a config file does not load the weights associated with the model, only the
|
||||
configuration.
|
||||
Check out the :meth:`~transformers.PreTrainedModel.from_pretrained` method to load the model weights.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ from torch.nn import CrossEntropyLoss
|
||||
|
||||
from .configuration_ctrl import CTRLConfig
|
||||
from .file_utils import add_start_docstrings, add_start_docstrings_to_callable
|
||||
from .modeling_utils import Conv1D, PreTrainedModel
|
||||
from .modeling_utils import Conv1D, PreTrainedModel, find_pruneable_heads_and_indices, prune_linear_layer
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -95,6 +95,24 @@ class MultiHeadAttention(torch.nn.Module):
|
||||
self.Wv = torch.nn.Linear(d_model_size, d_model_size)
|
||||
|
||||
self.dense = torch.nn.Linear(d_model_size, d_model_size)
|
||||
self.pruned_heads = set()
|
||||
|
||||
def prune_heads(self, heads):
|
||||
attention_head_size = self.d_model_size // self.num_heads
|
||||
if len(heads) == 0:
|
||||
return
|
||||
heads, index = find_pruneable_heads_and_indices(heads, self.num_heads, attention_head_size, self.pruned_heads)
|
||||
|
||||
# Prune linear layers
|
||||
self.Wq = prune_linear_layer(self.Wq, index)
|
||||
self.Wk = prune_linear_layer(self.Wk, index)
|
||||
self.Wv = prune_linear_layer(self.Wv, index)
|
||||
self.dense = prune_linear_layer(self.dense, index, dim=1)
|
||||
|
||||
# Update hyper params
|
||||
self.num_heads = self.num_heads - len(heads)
|
||||
self.d_model_size = attention_head_size * self.num_heads
|
||||
self.pruned_heads = self.pruned_heads.union(heads)
|
||||
|
||||
def split_into_heads(self, x, batch_size):
|
||||
x = x.reshape(batch_size, -1, self.num_heads, self.depth)
|
||||
@@ -266,6 +284,8 @@ CTRL_INPUTS_DOCSTRING = r"""
|
||||
use_cache (:obj:`bool`):
|
||||
If `use_cache` is True, `past` key value states are returned and
|
||||
can be used to speed up decoding (see `past`). Defaults to `True`.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
@@ -304,7 +324,7 @@ class CTRLModel(CTRLPreTrainedModel):
|
||||
heads_to_prune: dict of {layer_num: list of heads to prune in this layer}
|
||||
"""
|
||||
for layer, heads in heads_to_prune.items():
|
||||
self.h[layer].attn.prune_heads(heads)
|
||||
self.h[layer].multi_head_attention.prune_heads(heads)
|
||||
|
||||
@add_start_docstrings_to_callable(CTRL_INPUTS_DOCSTRING)
|
||||
def forward(
|
||||
@@ -332,7 +352,7 @@ class CTRLModel(CTRLPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -521,7 +541,7 @@ class CTRLLMHeadModel(CTRLPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
|
||||
@@ -377,6 +377,8 @@ DISTILBERT_INPUTS_DOCSTRING = r"""
|
||||
Optionally, instead of passing :obj:`input_ids` you can choose to directly pass an embedded representation.
|
||||
This is useful if you want more control over how to convert `input_ids` indices into associated vectors
|
||||
than the model's internal embedding lookup matrix.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
@@ -421,7 +423,7 @@ class DistilBertModel(DistilBertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -523,7 +525,7 @@ class DistilBertForMaskedLM(DistilBertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -616,7 +618,7 @@ class DistilBertForSequenceClassification(DistilBertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -713,7 +715,7 @@ class DistilBertForQuestionAnswering(DistilBertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -812,7 +814,7 @@ class DistilBertForTokenClassification(DistilBertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -846,7 +848,7 @@ class DistilBertForTokenClassification(DistilBertPreTrainedModel):
|
||||
sequence_output = self.dropout(sequence_output)
|
||||
logits = self.classifier(sequence_output)
|
||||
|
||||
outputs = (logits,) + outputs[2:] # add hidden states and attention if they are here
|
||||
outputs = (logits,) + outputs[1:] # add hidden states and attention if they are here
|
||||
if labels is not None:
|
||||
loss_fct = CrossEntropyLoss()
|
||||
# Only keep active parts of the loss
|
||||
@@ -862,3 +864,111 @@ class DistilBertForTokenClassification(DistilBertPreTrainedModel):
|
||||
outputs = (loss,) + outputs
|
||||
|
||||
return outputs # (loss), scores, (hidden_states), (attentions)
|
||||
|
||||
|
||||
@add_start_docstrings(
|
||||
"""DistilBert Model with a multiple choice classification head on top (a linear layer on top of
|
||||
the pooled output and a softmax) e.g. for RocStories/SWAG tasks. """,
|
||||
DISTILBERT_START_DOCSTRING,
|
||||
)
|
||||
class DistilBertForMultipleChoice(DistilBertPreTrainedModel):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
|
||||
self.distilbert = DistilBertModel(config)
|
||||
self.pre_classifier = nn.Linear(config.dim, config.dim)
|
||||
self.classifier = nn.Linear(config.dim, 1)
|
||||
self.dropout = nn.Dropout(config.seq_classif_dropout)
|
||||
|
||||
self.init_weights()
|
||||
|
||||
@add_start_docstrings_to_callable(DISTILBERT_INPUTS_DOCSTRING.format("(batch_size, num_choices, sequence_length)"))
|
||||
def forward(
|
||||
self,
|
||||
input_ids=None,
|
||||
attention_mask=None,
|
||||
head_mask=None,
|
||||
inputs_embeds=None,
|
||||
labels=None,
|
||||
output_attentions=None,
|
||||
):
|
||||
r"""
|
||||
labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size,)`, `optional`, defaults to :obj:`None`):
|
||||
Labels for computing the multiple choice classification loss.
|
||||
Indices should be in ``[0, ..., num_choices-1]`` where `num_choices` is the size of the second dimension
|
||||
of the input tensors. (see `input_ids` above)
|
||||
|
||||
Returns:
|
||||
:obj:`tuple(torch.FloatTensor)` comprising various elements depending on the configuration (:class:`~transformers.BertConfig`) and inputs:
|
||||
loss (:obj:`torch.FloatTensor` of shape `(1,)`, `optional`, returned when :obj:`labels` is provided):
|
||||
Classification loss.
|
||||
classification_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, num_choices)`):
|
||||
`num_choices` is the second dimension of the input tensors. (see `input_ids` above).
|
||||
|
||||
Classification scores (before SoftMax).
|
||||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``config.output_hidden_states=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for the output of the embeddings + one for the output of each layer)
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
||||
heads.
|
||||
|
||||
Examples::
|
||||
|
||||
from transformers import DistilBertTokenizer, DistilBertForMultipleChoice
|
||||
import torch
|
||||
|
||||
tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-cased')
|
||||
model = DistilBertForMultipleChoice.from_pretrained('distilbert-base-cased')
|
||||
|
||||
prompt = "In Italy, pizza served in formal settings, such as at a restaurant, is presented unsliced."
|
||||
choice0 = "It is eaten with a fork and a knife."
|
||||
choice1 = "It is eaten while held in the hand."
|
||||
labels = torch.tensor(0).unsqueeze(0) # choice0 is correct (according to Wikipedia ;)), batch size 1
|
||||
|
||||
encoding = tokenizer.batch_encode_plus([[prompt, choice0], [prompt, choice1]], return_tensors='pt', pad_to_max_length=True)
|
||||
outputs = model(**{k: v.unsqueeze(0) for k,v in encoding.items()}, labels=labels) # batch size is 1
|
||||
|
||||
# the linear classifier still needs to be trained
|
||||
loss, logits = outputs[:2]
|
||||
"""
|
||||
num_choices = input_ids.shape[1] if input_ids is not None else inputs_embeds.shape[1]
|
||||
|
||||
input_ids = input_ids.view(-1, input_ids.size(-1)) if input_ids is not None else None
|
||||
attention_mask = attention_mask.view(-1, attention_mask.size(-1)) if attention_mask is not None else None
|
||||
inputs_embeds = (
|
||||
inputs_embeds.view(-1, inputs_embeds.size(-2), inputs_embeds.size(-1))
|
||||
if inputs_embeds is not None
|
||||
else None
|
||||
)
|
||||
|
||||
outputs = self.distilbert(
|
||||
input_ids,
|
||||
attention_mask=attention_mask,
|
||||
head_mask=head_mask,
|
||||
inputs_embeds=inputs_embeds,
|
||||
output_attentions=output_attentions,
|
||||
)
|
||||
|
||||
hidden_state = outputs[0] # (bs * num_choices, seq_len, dim)
|
||||
pooled_output = hidden_state[:, 0] # (bs * num_choices, dim)
|
||||
pooled_output = self.pre_classifier(pooled_output) # (bs * num_choices, dim)
|
||||
pooled_output = nn.ReLU()(pooled_output) # (bs * num_choices, dim)
|
||||
pooled_output = self.dropout(pooled_output) # (bs * num_choices, dim)
|
||||
logits = self.classifier(pooled_output) # (bs * num_choices, 1)
|
||||
|
||||
reshaped_logits = logits.view(-1, num_choices) # (bs, num_choices)
|
||||
|
||||
outputs = (reshaped_logits,) + outputs[1:] # add hidden states and attention if they are here
|
||||
|
||||
if labels is not None:
|
||||
loss_fct = CrossEntropyLoss()
|
||||
loss = loss_fct(reshaped_logits, labels)
|
||||
outputs = (loss,) + outputs
|
||||
|
||||
return outputs # (loss), reshaped_logits, (hidden_states), (attentions)
|
||||
@@ -220,6 +220,8 @@ ELECTRA_INPUTS_DOCSTRING = r"""
|
||||
is used in the cross-attention if the model is configured as a decoder.
|
||||
Mask values selected in ``[0, 1]``:
|
||||
``1`` for tokens that are NOT MASKED, ``0`` for MASKED tokens.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
@@ -281,7 +283,7 @@ class ElectraModel(ElectraPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -404,7 +406,7 @@ class ElectraForSequenceClassification(ElectraPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -433,7 +435,7 @@ class ElectraForSequenceClassification(ElectraPreTrainedModel):
|
||||
sequence_output = discriminator_hidden_states[0]
|
||||
logits = self.classifier(sequence_output)
|
||||
|
||||
outputs = (logits,) + discriminator_hidden_states[2:] # add hidden states and attention if they are here
|
||||
outputs = (logits,) + discriminator_hidden_states[1:] # add hidden states and attention if they are here
|
||||
|
||||
if labels is not None:
|
||||
if self.num_labels == 1:
|
||||
@@ -494,7 +496,7 @@ class ElectraForPreTraining(ElectraPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -597,7 +599,7 @@ class ElectraForMaskedLM(ElectraPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -691,7 +693,7 @@ class ElectraForTokenClassification(ElectraPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -740,3 +742,121 @@ class ElectraForTokenClassification(ElectraPreTrainedModel):
|
||||
output += discriminator_hidden_states[1:]
|
||||
|
||||
return output # (loss), scores, (hidden_states), (attentions)
|
||||
|
||||
|
||||
@add_start_docstrings(
|
||||
"""
|
||||
ELECTRA Model with a span classification head on top for extractive question-answering tasks like SQuAD (a linear
|
||||
layers on top of the hidden-states output to compute `span start logits` and `span end logits`).""",
|
||||
ELECTRA_START_DOCSTRING,
|
||||
)
|
||||
class ElectraForQuestionAnswering(ElectraPreTrainedModel):
|
||||
config_class = ElectraConfig
|
||||
base_model_prefix = "electra"
|
||||
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
self.num_labels = config.num_labels
|
||||
|
||||
self.electra = ElectraModel(config)
|
||||
self.qa_outputs = nn.Linear(config.hidden_size, config.num_labels)
|
||||
|
||||
self.init_weights()
|
||||
|
||||
@add_start_docstrings_to_callable(ELECTRA_INPUTS_DOCSTRING.format("(batch_size, sequence_length)"))
|
||||
def forward(
|
||||
self,
|
||||
input_ids=None,
|
||||
attention_mask=None,
|
||||
token_type_ids=None,
|
||||
position_ids=None,
|
||||
head_mask=None,
|
||||
inputs_embeds=None,
|
||||
start_positions=None,
|
||||
end_positions=None,
|
||||
output_attentions=None,
|
||||
):
|
||||
r"""
|
||||
start_positions (:obj:`torch.LongTensor` of shape :obj:`(batch_size,)`, `optional`, defaults to :obj:`None`):
|
||||
Labels for position (index) of the start of the labelled span for computing the token classification loss.
|
||||
Positions are clamped to the length of the sequence (`sequence_length`).
|
||||
Position outside of the sequence are not taken into account for computing the loss.
|
||||
end_positions (:obj:`torch.LongTensor` of shape :obj:`(batch_size,)`, `optional`, defaults to :obj:`None`):
|
||||
Labels for position (index) of the end of the labelled span for computing the token classification loss.
|
||||
Positions are clamped to the length of the sequence (`sequence_length`).
|
||||
Position outside of the sequence are not taken into account for computing the loss.
|
||||
|
||||
Returns:
|
||||
:obj:`tuple(torch.FloatTensor)` comprising various elements depending on the configuration (:class:`~transformers.ElectraConfig`) and inputs:
|
||||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided):
|
||||
Total span extraction loss is the sum of a Cross-Entropy for the start and end positions.
|
||||
start_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length,)`):
|
||||
Span-start scores (before SoftMax).
|
||||
end_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length,)`):
|
||||
Span-end scores (before SoftMax).
|
||||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``config.output_hidden_states=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for the output of the embeddings + one for the output of each layer)
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
||||
heads.
|
||||
|
||||
Examples::
|
||||
|
||||
from transformers import ElectraTokenizer, ElectraForQuestionAnswering
|
||||
import torch
|
||||
|
||||
tokenizer = ElectraTokenizer.from_pretrained('google/electra-base-discriminator')
|
||||
model = ElectraForQuestionAnswering.from_pretrained('google/electra-base-discriminator')
|
||||
|
||||
question, text = "Who was Jim Henson?", "Jim Henson was a nice puppet"
|
||||
encoding = tokenizer.encode_plus(question, text, return_tensors='pt')
|
||||
input_ids, token_type_ids = encoding['input_ids'], encoding['token_type_ids']
|
||||
start_scores, end_scores = model(input_ids, token_type_ids=token_type_ids)
|
||||
|
||||
all_tokens = tokenizer.convert_ids_to_tokens(input_ids.squeeze(0))
|
||||
answer = ' '.join(all_tokens[torch.argmax(start_scores) : torch.argmax(end_scores)+1])
|
||||
|
||||
"""
|
||||
|
||||
discriminator_hidden_states = self.electra(
|
||||
input_ids,
|
||||
attention_mask=attention_mask,
|
||||
token_type_ids=token_type_ids,
|
||||
position_ids=position_ids,
|
||||
head_mask=head_mask,
|
||||
inputs_embeds=inputs_embeds,
|
||||
output_attentions=output_attentions,
|
||||
)
|
||||
|
||||
sequence_output = discriminator_hidden_states[0]
|
||||
|
||||
logits = self.qa_outputs(sequence_output)
|
||||
start_logits, end_logits = logits.split(1, dim=-1)
|
||||
start_logits = start_logits.squeeze(-1)
|
||||
end_logits = end_logits.squeeze(-1)
|
||||
|
||||
outputs = (start_logits, end_logits,) + discriminator_hidden_states[1:]
|
||||
if start_positions is not None and end_positions is not None:
|
||||
# If we are on multi-GPU, split add a dimension
|
||||
if len(start_positions.size()) > 1:
|
||||
start_positions = start_positions.squeeze(-1)
|
||||
if len(end_positions.size()) > 1:
|
||||
end_positions = end_positions.squeeze(-1)
|
||||
# sometimes the start/end positions are outside our model inputs, we ignore these terms
|
||||
ignored_index = start_logits.size(1)
|
||||
start_positions.clamp_(0, ignored_index)
|
||||
end_positions.clamp_(0, ignored_index)
|
||||
|
||||
loss_fct = CrossEntropyLoss(ignore_index=ignored_index)
|
||||
start_loss = loss_fct(start_logits, start_positions)
|
||||
end_loss = loss_fct(end_logits, end_positions)
|
||||
total_loss = (start_loss + end_loss) / 2
|
||||
outputs = (total_loss,) + outputs
|
||||
|
||||
return outputs # (loss), start_logits, end_logits, (hidden_states), (attentions)
|
||||
@@ -32,7 +32,7 @@ class EncoderDecoderModel(PreTrainedModel):
|
||||
instantiated as a transformer architecture with one of the base model
|
||||
classes of the library as encoder and another one as
|
||||
decoder when created with the `AutoModel.from_pretrained(pretrained_model_name_or_path)`
|
||||
class method for the encoder and `AutoModelWithLMHead.from_pretrained(pretrained_model_name_or_path)` class method for the decoder.
|
||||
class method for the encoder and `AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path)` class method for the decoder.
|
||||
"""
|
||||
config_class = EncoderDecoderConfig
|
||||
base_model_prefix = "encoder_decoder"
|
||||
@@ -61,9 +61,9 @@ class EncoderDecoderModel(PreTrainedModel):
|
||||
encoder = AutoModel.from_config(config.encoder)
|
||||
|
||||
if decoder is None:
|
||||
from transformers import AutoModelWithLMHead
|
||||
from transformers import AutoModelForCausalLM
|
||||
|
||||
decoder = AutoModelWithLMHead.from_config(config.decoder)
|
||||
decoder = AutoModelForCausalLM.from_config(config.decoder)
|
||||
|
||||
self.encoder = encoder
|
||||
self.decoder = decoder
|
||||
@@ -157,7 +157,7 @@ class EncoderDecoderModel(PreTrainedModel):
|
||||
assert (
|
||||
decoder_pretrained_model_name_or_path is not None
|
||||
), "If `decoder_model` is not defined as an argument, a `decoder_pretrained_model_name_or_path` has to be defined"
|
||||
from .modeling_auto import AutoModelWithLMHead
|
||||
from .modeling_auto import AutoModelForCausalLM
|
||||
|
||||
if "config" not in kwargs_decoder:
|
||||
from transformers import AutoConfig
|
||||
@@ -176,7 +176,7 @@ class EncoderDecoderModel(PreTrainedModel):
|
||||
f"Decoder model {decoder_pretrained_model_name_or_path} is not initialized as a decoder. In order to initialize {decoder_pretrained_model_name_or_path} as a decoder, make sure that the attribute `is_decoder` of `decoder_config` passed to `.from_encoder_decoder_pretrained(...)` is set to `True` or do not pass a `decoder_config` to `.from_encoder_decoder_pretrained(...)`"
|
||||
)
|
||||
|
||||
decoder = AutoModelWithLMHead.from_pretrained(decoder_pretrained_model_name_or_path, **kwargs_decoder)
|
||||
decoder = AutoModelForCausalLM.from_pretrained(decoder_pretrained_model_name_or_path, **kwargs_decoder)
|
||||
|
||||
return cls(encoder=encoder, decoder=decoder)
|
||||
|
||||
@@ -192,7 +192,6 @@ class EncoderDecoderModel(PreTrainedModel):
|
||||
decoder_head_mask=None,
|
||||
decoder_inputs_embeds=None,
|
||||
labels=None,
|
||||
lm_labels=None,
|
||||
**kwargs,
|
||||
):
|
||||
|
||||
@@ -239,11 +238,6 @@ class EncoderDecoderModel(PreTrainedModel):
|
||||
Indices should be in ``[-100, 0, ..., config.vocab_size]`` (see ``input_ids`` docstring)
|
||||
Tokens with indices set to ``-100`` are ignored (masked), the loss is only computed for the tokens with labels
|
||||
in ``[0, ..., config.vocab_size]``
|
||||
lm_labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`, defaults to :obj:`None`):
|
||||
Labels for computing the left-to-right language modeling loss (next word prediction) for the decoder.
|
||||
Indices should be in ``[-100, 0, ..., config.vocab_size]`` (see ``input_ids`` docstring)
|
||||
Tokens with indices set to ``-100`` are ignored (masked), the loss is only computed for the tokens with labels
|
||||
in ``[0, ..., config.vocab_size]``
|
||||
kwargs: (`optional`) Remaining dictionary of keyword arguments. Keyword arguments come in two flavors:
|
||||
- Without a prefix which will be input as `**encoder_kwargs` for the encoder forward function.
|
||||
- With a `decoder_` prefix which will be input as `**decoder_kwargs` for the decoder forward function.
|
||||
@@ -293,7 +287,6 @@ class EncoderDecoderModel(PreTrainedModel):
|
||||
encoder_hidden_states=hidden_states,
|
||||
encoder_attention_mask=attention_mask,
|
||||
head_mask=decoder_head_mask,
|
||||
lm_labels=lm_labels,
|
||||
labels=labels,
|
||||
**kwargs_decoder,
|
||||
)
|
||||
@@ -305,7 +298,7 @@ class EncoderDecoderModel(PreTrainedModel):
|
||||
|
||||
# first step
|
||||
if type(past) is tuple:
|
||||
encoder_outputs = past
|
||||
encoder_outputs, _ = past
|
||||
else:
|
||||
encoder_outputs = (past,)
|
||||
|
||||
|
||||
@@ -100,6 +100,8 @@ FLAUBERT_INPUTS_DOCSTRING = r"""
|
||||
Optionally, instead of passing :obj:`input_ids` you can choose to directly pass an embedded representation.
|
||||
This is useful if you want more control over how to convert `input_ids` indices into associated vectors
|
||||
than the model's internal embedding lookup matrix.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
@@ -140,7 +142,7 @@ class FlaubertModel(XLMModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
|
||||
@@ -335,6 +335,8 @@ GPT2_INPUTS_DOCSTRING = r"""
|
||||
If `past` is used, optionally only the last `inputs_embeds` have to be input (see `past`).
|
||||
use_cache (:obj:`bool`):
|
||||
If `use_cache` is True, `past` key value states are returned and can be used to speed up decoding (see `past`). Defaults to `True`.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
@@ -395,7 +397,7 @@ class GPT2Model(GPT2PreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -582,7 +584,7 @@ class GPT2LMHeadModel(GPT2PreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -703,7 +705,7 @@ class GPT2DoubleHeadsModel(GPT2PreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
|
||||
@@ -435,7 +435,7 @@ class LongformerSelfAttention(nn.Module):
|
||||
|
||||
LONGFORMER_START_DOCSTRING = r"""
|
||||
|
||||
This model is a PyTorch `torch.nn.Module <https://pytorch.org/docs/stable/nn.html#torch.nn.Module>`_ sub-class.
|
||||
This model is a PyTorch `torch.nn.Module <https://pytorch.org/docs/stable/nn.html#torch.nn.Module>`__ sub-class.
|
||||
Use it as a regular PyTorch Module and refer to the PyTorch documentation for all matter related to general
|
||||
usage and behavior.
|
||||
|
||||
@@ -467,7 +467,7 @@ LONGFORMER_INPUTS_DOCSTRING = r"""
|
||||
Tokens with global attention attends to all other tokens, and all other tokens attend to them. This is important for
|
||||
task-specific finetuning because it makes the model more flexible at representing the task. For example,
|
||||
for classification, the <s> token should be given global attention. For QA, all question tokens should also have
|
||||
global attention. Please refer to the Longformer paper https://arxiv.org/abs/2004.05150 for more details.
|
||||
global attention. Please refer to the `Longformer paper <https://arxiv.org/abs/2004.05150>`__ for more details.
|
||||
Mask values selected in ``[0, 1]``:
|
||||
``0`` for local attention (a sliding window attention),
|
||||
``1`` for global attention (tokens that attend to all other tokens, and all other tokens attend to them).
|
||||
@@ -487,6 +487,8 @@ LONGFORMER_INPUTS_DOCSTRING = r"""
|
||||
Optionally, instead of passing :obj:`input_ids` you can choose to directly pass an embedded representation.
|
||||
This is useful if you want more control over how to convert `input_ids` indices into associated vectors
|
||||
than the model's internal embedding lookup matrix.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
@@ -498,7 +500,7 @@ class LongformerModel(RobertaModel):
|
||||
"""
|
||||
This class overrides :class:`~transformers.RobertaModel` to provide the ability to process
|
||||
long sequences following the selfattention approach described in `Longformer: the Long-Document Transformer
|
||||
<https://arxiv.org/abs/2004.05150>`_ by Iz Beltagy, Matthew E. Peters, and Arman Cohan. Longformer selfattention
|
||||
<https://arxiv.org/abs/2004.05150>`__ by Iz Beltagy, Matthew E. Peters, and Arman Cohan. Longformer selfattention
|
||||
combines a local (sliding window) and global attention to extend to long documents without the O(n^2) increase in
|
||||
memory and compute.
|
||||
|
||||
@@ -597,7 +599,7 @@ class LongformerModel(RobertaModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -726,7 +728,7 @@ class LongformerForMaskedLM(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -795,6 +797,8 @@ class LongformerForSequenceClassification(BertPreTrainedModel):
|
||||
self.longformer = LongformerModel(config)
|
||||
self.classifier = LongformerClassificationHead(config)
|
||||
|
||||
self.init_weights()
|
||||
|
||||
@add_start_docstrings_to_callable(LONGFORMER_INPUTS_DOCSTRING.format("(batch_size, sequence_length)"))
|
||||
def forward(
|
||||
self,
|
||||
@@ -825,7 +829,7 @@ class LongformerForSequenceClassification(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -859,6 +863,7 @@ class LongformerForSequenceClassification(BertPreTrainedModel):
|
||||
token_type_ids=token_type_ids,
|
||||
position_ids=position_ids,
|
||||
inputs_embeds=inputs_embeds,
|
||||
output_attentions=output_attentions,
|
||||
)
|
||||
sequence_output = outputs[0]
|
||||
logits = self.classifier(sequence_output)
|
||||
@@ -917,7 +922,7 @@ class LongformerForQuestionAnswering(BertPreTrainedModel):
|
||||
@add_start_docstrings_to_callable(LONGFORMER_INPUTS_DOCSTRING.format("(batch_size, sequence_length)"))
|
||||
def forward(
|
||||
self,
|
||||
input_ids,
|
||||
input_ids=None,
|
||||
attention_mask=None,
|
||||
global_attention_mask=None,
|
||||
token_type_ids=None,
|
||||
@@ -948,7 +953,7 @@ class LongformerForQuestionAnswering(BertPreTrainedModel):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for the output of the embeddings + one for the output of each layer)
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
||||
@@ -1069,7 +1074,7 @@ class LongformerForTokenClassification(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -1097,6 +1102,7 @@ class LongformerForTokenClassification(BertPreTrainedModel):
|
||||
token_type_ids=token_type_ids,
|
||||
position_ids=position_ids,
|
||||
inputs_embeds=inputs_embeds,
|
||||
output_attentions=output_attentions,
|
||||
)
|
||||
|
||||
sequence_output = outputs[0]
|
||||
@@ -1172,7 +1178,7 @@ class LongformerForMultipleChoice(BertPreTrainedModel):
|
||||
of shape :obj:`(batch_size, sequence_length, hidden_size)`.
|
||||
|
||||
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True``):
|
||||
attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or ``config.output_attentions=True``):
|
||||
Tuple of :obj:`torch.FloatTensor` (one for each layer) of shape
|
||||
:obj:`(batch_size, num_heads, sequence_length, sequence_length)`.
|
||||
|
||||
@@ -1196,7 +1202,7 @@ class LongformerForMultipleChoice(BertPreTrainedModel):
|
||||
loss, classification_scores = outputs[:2]
|
||||
|
||||
"""
|
||||
num_choices = input_ids.shape[1]
|
||||
num_choices = input_ids.shape[1] if input_ids is not None else inputs_embeds.shape[1]
|
||||
|
||||
# set global attention on question tokens
|
||||
if global_attention_mask is None:
|
||||
@@ -1210,7 +1216,7 @@ class LongformerForMultipleChoice(BertPreTrainedModel):
|
||||
dim=1,
|
||||
)
|
||||
|
||||
flat_input_ids = input_ids.view(-1, input_ids.size(-1))
|
||||
flat_input_ids = input_ids.view(-1, input_ids.size(-1)) if input_ids is not None else None
|
||||
flat_position_ids = position_ids.view(-1, position_ids.size(-1)) if position_ids is not None else None
|
||||
flat_token_type_ids = token_type_ids.view(-1, token_type_ids.size(-1)) if token_type_ids is not None else None
|
||||
flat_attention_mask = attention_mask.view(-1, attention_mask.size(-1)) if attention_mask is not None else None
|
||||
@@ -1219,6 +1225,11 @@ class LongformerForMultipleChoice(BertPreTrainedModel):
|
||||
if global_attention_mask is not None
|
||||
else None
|
||||
)
|
||||
flat_inputs_embeds = (
|
||||
inputs_embeds.view(-1, inputs_embeds.size(-2), inputs_embeds.size(-1))
|
||||
if inputs_embeds is not None
|
||||
else None
|
||||
)
|
||||
|
||||
outputs = self.longformer(
|
||||
flat_input_ids,
|
||||
@@ -1226,6 +1237,8 @@ class LongformerForMultipleChoice(BertPreTrainedModel):
|
||||
token_type_ids=flat_token_type_ids,
|
||||
attention_mask=flat_attention_mask,
|
||||
global_attention_mask=flat_global_attention_mask,
|
||||
inputs_embeds=flat_inputs_embeds,
|
||||
output_attentions=output_attentions,
|
||||
)
|
||||
pooled_output = outputs[1]
|
||||
|
||||
|
||||
@@ -141,6 +141,8 @@ MMBT_INPUTS_DOCSTRING = r""" Inputs:
|
||||
is used in the cross-attention if the model is configured as a decoder.
|
||||
Mask values selected in ``[0, 1]``:
|
||||
``1`` for tokens that are NOT MASKED, ``0`` for MASKED tokens.
|
||||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`None`):
|
||||
If set to ``True``, the attentions tensors of all attention layers are returned. See ``attentions`` under returned tensors for more detail.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
Loaded 100 of 192 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user