Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a4ae4954e | ||
|
|
58cca47c16 | ||
|
|
991172922f | ||
|
|
b58a15a31e | ||
|
|
fedabcd154 | ||
|
|
17ade127b9 | ||
|
|
814ed7ee76 | ||
|
|
49281ac939 | ||
|
|
97355339f6 | ||
|
|
55b932a818 | ||
|
|
21cd8c4086 | ||
|
|
8438bab38e | ||
|
|
6b735a7253 |
@@ -142,6 +142,7 @@ conversion utilities for the following models:
|
||||
preprocessing
|
||||
training
|
||||
model_sharing
|
||||
tokenizer_summary
|
||||
multilingual
|
||||
|
||||
.. toctree::
|
||||
|
||||
@@ -146,8 +146,9 @@ Using the tokenizer
|
||||
|
||||
We mentioned the tokenizer is responsible for the preprocessing of your texts. First, it will split a given text in
|
||||
words (or part of words, punctuation symbols, etc.) usually called `tokens`. There are multiple rules that can govern
|
||||
that process, which is why we need to instantiate the tokenizer using the name of the model, to make sure we use the
|
||||
same rules as when the model was pretrained.
|
||||
that process (you can learn more about them in the :doc:`tokenizer_summary <tokenizer_summary>`, which is why we need
|
||||
to instantiate the tokenizer using the name of the model, to make sure we use the same rules as when the model was
|
||||
pretrained.
|
||||
|
||||
The second step is to convert those `tokens` into numbers, to be able to build a tensor out of them and feed them to
|
||||
the model. To do this, the tokenizer has a `vocab`, which is the part we download when we instantiate it with the
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
Tokenizer summary
|
||||
-----------------
|
||||
|
||||
In this page, we will have a closer look at tokenization. As we saw in
|
||||
:doc:`the preprocessing tutorial <preprocessing>`, tokenizing a text is splitting it into words or subwords, which then
|
||||
are converted to ids. The second part is pretty straightforward, here we will focus on the first part. More
|
||||
specifically, we will look at the three main different kinds of tokenizers used in 🤗 Transformers:
|
||||
:ref:`Byte-Pair Encoding (BPE) <byte-pair-encoding>`, :ref:`WordPiece <wordpiece>` and
|
||||
:ref:`SentencePiece <sentencepiece>`, and provide examples of models using each of those.
|
||||
|
||||
Note that on each model page, you can look at the documentation of the associated tokenizer to know which of those
|
||||
algorithms the pretrained model used. For instance, if we look at :class:`~transformers.BertTokenizer`, we can see it's
|
||||
using :ref:`WordPiece <wordpiece>`.
|
||||
|
||||
Introduction to tokenization
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Splitting a text in smaller chunks is a task that's harder than it looks, and there are multiple ways of doing it. For
|
||||
instance, let's look at the sentence "Don't you love 🤗 Transformers? We sure do." A first simple way of tokenizing
|
||||
this text is just to split it by spaces, which would give:
|
||||
|
||||
::
|
||||
|
||||
["Don't", "you", "love", "🤗", "Transformers?", "We", "sure", "do."]
|
||||
|
||||
This is a nice first step, but if we look at the tokens "Transformers?" or "do.", we can see we can do better. Those
|
||||
will be different than the tokens "Transformers" and "do" for our model, so we should probably take the punctuation
|
||||
into account. This would give:
|
||||
|
||||
::
|
||||
|
||||
["Don", "'", "t", "you", "love", "🤗", "Transformers", "?", "We", "sure", "do", "."]
|
||||
|
||||
which is better already. One thing that is annoying though is how it dealt with "Don't". "Don't" stands for do not, so
|
||||
it should probably be better tokenized as ``["Do", "n't"]``. This is where things start getting more complicated, and
|
||||
part of the reason each kind of model has its own tokenizer class. Depending on the rules we apply to split our texts
|
||||
into tokens, we'll get different tokenized versions of the same text. And of course, a given pretrained model won't
|
||||
perform properly if you don't use the exact same rules as the persons who pretrained it.
|
||||
|
||||
`spaCy <https://spacy.io/>`__ and `Moses <http://www.statmt.org/moses/?n=Development.GetStarted>`__ are two popular
|
||||
rule-based tokenizers. On the text above, they'd output something like:
|
||||
|
||||
::
|
||||
|
||||
["Do", "n't", "you", "love", "🤗", "Transformers", "?", "We", "sure", "do", "."]
|
||||
|
||||
Space/punctuation-tokenization and rule-based tokenization are both examples of word tokenization, which is splitting a
|
||||
sentence into words. While it's the most intuitive way to separate texts in smaller chunks, it can have a problem when
|
||||
you have a huge corpus: it usually yields a very big vocabulary (the set of all unique tokens used).
|
||||
:doc:`Transformer XL <model_doc/transformerxl>` for instance uses space/punctuation-tokenization, and has a vocabulary
|
||||
size of 267,735!
|
||||
|
||||
A huge vocabulary size means a huge embedding matrix at the start of the model, which will cause memory problems.
|
||||
TransformerXL deals with it by using a special kind of embeddings called adaptive embeddings, but in general,
|
||||
transformers model rarely have a vocabulary size greater than 50,000, especially if they are trained on a single
|
||||
language.
|
||||
|
||||
So if tokenizing on words is unsatisfactory, we could go on the opposite direction and simply tokenize on characters.
|
||||
While it's very simple and would save a lot of memory, this doesn't allow the model to learn representations of texts
|
||||
as meaningful as when using a word tokenization, leading to a loss of performance. So to get the best of both worlds,
|
||||
all transformers models use a hybrid between word-level and character-level tokenization called subword tokenization.
|
||||
|
||||
Subword tokenization
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Subword tokenization algorithms rely on the principle that most common words should be left as is, but rare words
|
||||
should be decomposed in meaningful subword units. For instance "annoyingly" might be considered a rare word and
|
||||
decomposed as "annoying" and "ly". This is especially useful in agglutinative languages such as Turkish, where you can
|
||||
form (almost) arbitrarily long complex words by stringing together some subwords.
|
||||
|
||||
This allows the model to keep a reasonable vocabulary while still learning useful representations for common words or
|
||||
subwords. This also gives the ability to the model to process words it has never seen before, by decomposing them into
|
||||
subwords it knows. For instance, the base :class:`~transformers.BertTokenizer` will tokenize "I have a new GPU!" like
|
||||
this:
|
||||
|
||||
::
|
||||
|
||||
>>> from transformers import BertTokenizer
|
||||
>>> tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
|
||||
>>> tokenizer.tokenize("I have a new GPU!")
|
||||
['i', 'have', 'a', 'new', 'gp', '##u', '!']
|
||||
|
||||
Since we are considering the uncased model, the sentence was lowercased first. Then all the words were present in the
|
||||
vocabulary of the tokenizer, except for "gpu", so the tokenizer split it in subwords it knows: "gp" and "##u". The "##"
|
||||
means that the rest of the token should be attached to the previous one, without space (for when we need to decode
|
||||
predictions and reverse the tokenization).
|
||||
|
||||
Another example is when we use the base :class:`~transformers.XLNetTokenizer` to tokenize our previous text:
|
||||
|
||||
::
|
||||
|
||||
>>> from transformers import XLNetTokenizer
|
||||
>>> tokenizer = XLNetTokenizer.from_pretrained('xlnet-base-cased')
|
||||
>>> tokenizer.tokenize("Don't you love 🤗 Transformers? We sure do.")
|
||||
['▁Don', "'", 't', '▁you', '▁love', '▁', '🤗', '▁', 'Transform', 'ers', '?', '▁We', '▁sure', '▁do', '.']
|
||||
|
||||
We'll get back to the meaning of those '▁' when we look at :ref:`SentencePiece <sentencepiece>` but you can see
|
||||
Transformers has been split into "Transform" and "ers".
|
||||
|
||||
Let's now look at how the different subword tokenization algorithms work. Note that they all rely on some form of
|
||||
training which is usually done on the corpus the corresponding model will be trained on.
|
||||
|
||||
.. _byte-pair-encoding:
|
||||
|
||||
Byte-Pair Encoding
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Byte-Pair Encoding was introduced in `this paper <https://arxiv.org/abs/1508.07909>`__. It relies on a pretokenizer
|
||||
splitting the training data into words, which can be a simple space tokenization
|
||||
(:doc:`GPT-2 <model_doc/gpt2>` and :doc:`Roberta <model_doc/roberta>` uses this for instance) or a rule-based tokenizer
|
||||
(:doc:`XLM <model_doc/xlm>` use Moses for most languages, as does :doc:`FlauBERT <model_doc/flaubert>`),
|
||||
|
||||
:doc:`GPT <model_doc/gpt>` uses Spacy and ftfy) and, counts the frequency of each word in the training corpus.
|
||||
|
||||
It then begins from the list of all characters, and will learn merge rules to form a new token from two symbols in the
|
||||
vocabulary until it has learned a vocabulary of the desired size (this is a hyperparameter to pick).
|
||||
|
||||
Let's say that after the pre-tokenization we have the following words (the number indicating the frequency of each
|
||||
word):
|
||||
|
||||
::
|
||||
|
||||
('hug', 10), ('pug', 5), ('pun', 12), ('bun', 4), ('hugs', 5)
|
||||
|
||||
Then the base vocabulary is ['b', 'g', 'h', 'n', 'p', 's', 'u'] and all our words are first split by character:
|
||||
|
||||
::
|
||||
|
||||
('h' 'u' 'g', 10), ('p' 'u' 'g', 5), ('p' 'u' 'n', 12), ('b' 'u' 'n', 4), ('h' 'u' 'g' 's', 5)
|
||||
|
||||
We then take each pair of symbols and look at the most frequent. For instance 'hu' is present `10 + 5 = 15` times (10
|
||||
times in the 10 occurrences of 'hug', 5 times in the 5 occurrences of 'hugs'). The most frequent here is 'ug', present
|
||||
`10 + 5 + 2 + 5 = 22` times in total. So the first merge rule the tokenizer learns is to group all 'u' and 'g' together
|
||||
then it adds 'ug' to the vocabulary. Our corpus then becomes
|
||||
|
||||
::
|
||||
|
||||
('h' 'ug', 10), ('p' 'ug', 5), ('p' 'u' 'n', 12), ('b' 'u' 'n', 4), ('h' 'ug' 's', 5)
|
||||
|
||||
and we continue by looking at the next most common pair of symbols. It's 'un', present 16 times, so we merge those two
|
||||
and add 'un' to the vocabulary. Then it's 'hug' (as 'h' + 'ug'), present 15 times, so we merge those two and add 'hug'
|
||||
to the vocabulary.
|
||||
|
||||
At this stage, the vocabulary is ``['b', 'g', 'h', 'n', 'p', 's', 'u', 'ug', 'un', 'hug']`` and our corpus is
|
||||
represented as
|
||||
|
||||
::
|
||||
|
||||
('hug', 10), ('p' 'ug', 5), ('p' 'un', 12), ('b' 'un', 4), ('hug' 's', 5)
|
||||
|
||||
If we stop there, the tokenizer can apply the rules it learned to new words (as long as they don't contain characters that
|
||||
were not in the base vocabulary). For instance 'bug' would be tokenized as ``['b', 'ug']`` but mug would be tokenized as
|
||||
``['<unk>', 'ug']`` since the 'm' is not in the base vocabulary. This doesn't happen to letters in general (since the
|
||||
base corpus uses all of them), but to special characters like emojis.
|
||||
|
||||
As we said before, the vocabulary size (which is the base vocabulary size + the number of merges) is a hyperparameter
|
||||
to choose. For instance :doc:`GPT <model_doc/gpt>` has a vocabulary size of 40,478 since they have 478 base characters
|
||||
and chose to stop the training of the tokenizer at 40,000 merges.
|
||||
|
||||
Byte-level BPE
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
To deal with the fact the base vocabulary needs to get all base characters, which can be quite big if one allows for
|
||||
all unicode characters, the
|
||||
`GPT-2 paper <https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf>`__
|
||||
introduces a clever trick, which is to use bytes as the base vocabulary (which gives a size of 256). With some
|
||||
additional rules to deal with punctuation, this manages to be able to tokenize every text without needing an unknown
|
||||
token. For instance, the :doc:`GPT-2 model <model_doc/gpt>` has a vocabulary size of 50,257, which corresponds to the
|
||||
256 bytes base tokens, a special end-of-text token and the symbols learned with 50,000 merges.
|
||||
|
||||
.. _wordpiece:
|
||||
|
||||
WordPiece
|
||||
=========
|
||||
|
||||
WordPiece is the subword tokenization algorithm used for :doc:`BERT <model_doc/bert>` (as well as
|
||||
:doc:`DistilBERT <model_doc/distilbert>` and :doc:`Electra <model_doc/electra>`) and was outlined in
|
||||
`this paper <https://static.googleusercontent.com/media/research.google.com/ja//pubs/archive/37842.pdf>`__. It relies
|
||||
on the same base as BPE, which is to initialize the vocabulary to every character present in the corpus and
|
||||
progressively learn a given number of merge rules, the difference is that it doesn't choose the pair that is the most
|
||||
frequent but the one that will maximize the likelihood on the corpus once merged.
|
||||
|
||||
What does this mean? Well, in the previous example, it means we would only merge 'u' and 'g' if the probability of
|
||||
having 'ug' divided by the probability of having 'u' then 'g' is greater than for any other pair of symbols. It's
|
||||
subtly different from what BPE does in the sense that it evaluates what it "loses" by merging two symbols and makes
|
||||
sure it's `worth it`.
|
||||
|
||||
.. _unigram:
|
||||
|
||||
Unigram
|
||||
=======
|
||||
|
||||
Unigram is a subword tokenization algorithm introduced in `this paper <https://arxiv.org/pdf/1804.10959.pdf>`__.
|
||||
Instead of starting with a group of base symbols and learning merges with some rule, like BPE or WordPiece, it starts
|
||||
from a large vocabulary (for instance, all pretokenized words and the most common substrings) that it will trim down
|
||||
progressively. It's not used directly for any of the pretrained models in the library, but it's used in conjunction
|
||||
with :ref:`SentencePiece <sentencepiece>`.
|
||||
|
||||
More specifically, at a given step, unigram computes a loss from the corpus we have and the current vocabulary, then,
|
||||
for each subword, evaluate how much the loss would augment if the subword was removed from the vocabulary. It then
|
||||
sorts the subwords by this quantity (that represents how worse the loss becomes if the token is removed) and removes
|
||||
all the worst p tokens (for instance p could be 10% or 20%). It then repeats the process until the vocabulary has
|
||||
reached the desired size, always keeping the base characters (to be able to tokenize any word written with them, like
|
||||
BPE or WordPiece).
|
||||
|
||||
Contrary to BPE and WordPiece that work out rules in a certain order that you can then apply in the same order when
|
||||
tokenizing new text, Unigram will have several ways of tokenizing a new text. For instance, if it ends up with the
|
||||
vocabulary
|
||||
|
||||
::
|
||||
|
||||
['b', 'g', 'h', 'n', 'p', 's', 'u', 'ug', 'un', 'hug']
|
||||
|
||||
we had before, it could tokenize "hugs" as ``['hug', 's']``, ``['h', 'ug', 's']`` or ``['h', 'u', 'g', 's']``. So which
|
||||
one choose? On top of saving the vocabulary, the trained tokenizer will save the probability of each token in the
|
||||
training corpus. You can then give a probability to each tokenization (which is the product of the probabilities of the
|
||||
tokens forming it) and pick the most likely one (or if you want to apply some data augmentation, you could sample one
|
||||
of the tokenization according to their probabilities).
|
||||
|
||||
Those probabilities are what are used to define the loss that trains the tokenizer: if our corpus consists of the
|
||||
words :math:`x_{1}, \dots, x_{N}` and if for the word :math:`x_{i}` we note :math:`S(x_{i})` the set of all possible
|
||||
tokenizations of :math:`x_{i}` (with the current vocabulary), then the loss is defined as
|
||||
|
||||
.. math::
|
||||
\mathcal{L} = -\sum_{i=1}^{N} \log \left ( \sum_{x \in S(x_{i})} p(x) \right )
|
||||
|
||||
.. _sentencepiece:
|
||||
|
||||
SentencePiece
|
||||
=============
|
||||
|
||||
All the methods we have been looking at so far required some from of pretrokenization, which has a central problem: not
|
||||
all languages use spaces to separate words. This is a problem :doc:`XLM <model_doc/xlm>` solves by using specific
|
||||
pretokenizers for each of those languages (in this case, Chinese, Japanese and Thai). To solve this problem,
|
||||
SentencePiece (introduced in `this paper <https://arxiv.org/pdf/1808.06226.pdf>`__) treats the input as a raw stream,
|
||||
includes the space in the set of characters to use, then uses BPE or unigram to construct the appropriate vocabulary.
|
||||
|
||||
That's why in the example we saw before using :class:`~transformers.XLNetTokenizer` (which uses SentencePiece), we had
|
||||
some '▁' characters, that represent spaces. Decoding a tokenized text is then super easy: we just have to concatenate
|
||||
all of them together and replace those '▁' by spaces.
|
||||
|
||||
All transformers models in the library that use SentencePiece use it with unigram. Examples of models using it are
|
||||
:doc:`ALBERT <model_doc/albert>`, :doc:`XLNet <model_doc/xlnet>` or the :doc:`Marian framework <model_doc/marian>`.
|
||||
@@ -3,12 +3,13 @@ language: setswana
|
||||
---
|
||||
|
||||
# TswanaBert
|
||||
Pretrained model on the Tswana language using a masked language modeling (MLM) objective.
|
||||
|
||||
## Model Description.
|
||||
TswanaBERT is a transformers model pretrained on a corpus of Setswana data in a self-supervised fashion by masking part of the input words and training to predict the masks.
|
||||
TswanaBERT is a transformer model pre-trained on a corpus of Setswana in a self-supervised fashion by masking part of the input words and training to predict the masks by using byte-level tokens.
|
||||
|
||||
## Intended uses & limitations
|
||||
The model can be used for either masked language modeling or next word prediction. it can also be fine-tuned for a specifict application.
|
||||
The model can be used for either masked language modeling or next word prediction. It can also be fine-tuned on a specific down-stream NLP application.
|
||||
|
||||
#### How to use
|
||||
|
||||
@@ -44,13 +45,15 @@ The model can be used for either masked language modeling or next word predicti
|
||||
```
|
||||
|
||||
#### Limitations and bias
|
||||
The model is trained on a fairly small collection of setwana, mostly from news articles and creative writtings, and so is not representative enough of the language as yet.
|
||||
The model is trained on a relatively small collection of setwana, mostly from news articles and creative writtings, and so is not representative enough of the language as yet.
|
||||
|
||||
## Training data
|
||||
|
||||
The largest portion of this dataset (10k) lines of text, comes from the [Leipzig Corpora Collection](https://wortschatz.uni-leipzig.de/en/download)
|
||||
1. The largest portion of this dataset (10k) sentences of text, comes from the [Leipzig Corpora Collection](https://wortschatz.uni-leipzig.de/en/download)
|
||||
|
||||
The I then added 200 more phrases and sentences by scrapping following sites. I continue to expand the dataset
|
||||
2. I Then added SABC news headlines collected by Marivate Vukosi, & Sefara Tshephisho, (2020) that is generously made available on [zenoodo](http://doi.org/10.5281/zenodo.3668495 ). This added 185 tswana sentences to my corpus.
|
||||
|
||||
3. I went on to add 300 more sentences by scrapping following news sites and blogs that mosty originate in Botswana. I actively continue to expand the dataset.
|
||||
|
||||
* http://setswana.blogspot.com/
|
||||
* https://omniglot.com/writing/tswana.php
|
||||
@@ -58,10 +61,9 @@ The I then added 200 more phrases and sentences by scrapping following sites. I
|
||||
* http://www.mmegi.bw/index.php
|
||||
* https://tsena.co.bw
|
||||
* http://www.botswana.co.za/Cultural_Issues-travel/botswana-country-guide-en-route.html
|
||||
|
||||
## Training procedure
|
||||
The model was trained on a google colab Tesla T4 GPU for 200 epochs with a batch size of 64, on 13446 learned tokens.
|
||||
Other model training configuration setting can be found [here](https://s3.amazonaws.com/models.huggingface.co/bert/MoseliMotsoehli/TswanaBert/config.json)
|
||||
* https://www.poemhunter.com/poem/2013-setswana/
|
||||
https://www.poemhunter.com/poem/ngwana-wa-mosetsana/
|
||||
|
||||
|
||||
### BibTeX entry and citation info
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ This GPT-2 (774M) model is capable of generating abstracts given paper titles. I
|
||||
|
||||
#### How to use
|
||||
|
||||
To generate paper abstracts, use the provided `generate.py` [here](https://gist.github.com/chrisliu298/ccb8144888eace069da64ad3e6472d64). This is very similar to the HuggingFace's `run_generation.py` [here](https://github.com/huggingface/transformers/tree/master/examples/text-generation). You can simply replace the text with with your own model path (line 89) and change the input string to your paper title (line 127).
|
||||
To generate paper abstracts, use the provided `generate.py` [here](https://gist.github.com/chrisliu298/ccb8144888eace069da64ad3e6472d64). This is very similar to the HuggingFace's `run_generation.py` [here](https://github.com/huggingface/transformers/tree/master/examples/text-generation). You can simply replace the text with with your own model path (line 89) and change the input string to your paper title (line 127). If you want to use your own script, make sure to prepend `<|startoftext|> ` at the front and append ` <|sep|>` at the end of the paper title.
|
||||
|
||||
## Training data
|
||||
I selected a subset of the [arXiv Archive](https://github.com/staeiou/arxiv_archive) dataset (Geiger, 2019) as the training and evaluation data to fine-tune GPT-2. The original arXiv Archive dataset contains a full archive of metadata about papers on arxiv.org, from the start of the site in 1993 to the end of 2019. Our subset includes all the paper titles (query) and abstracts (context) under the Artificial Intelligence (cs.AI), Machine Learning (cs.LG), Computation and Language (cs.CL), and Computer Vision and Pattern Recognition (cs.CV) categories. I provide the information of the sub-dataset and the distribution of the training and evaluation dataset as follows.
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
language: english
|
||||
---
|
||||
|
||||
# Electra small ⚡ + SQuAD v2 ❓
|
||||
|
||||
[Electra-small-discriminator](https://huggingface.co/google/electra-small-discriminator) fine-tuned on [SQUAD v2.0 dataset](https://rajpurkar.github.io/SQuAD-explorer/explore/v2.0/dev/) for **Q&A** downstream task.
|
||||
|
||||
## Details of the downstream task (Q&A) - Model 🧠
|
||||
|
||||
**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](https://arxiv.org/pdf/1406.2661.pdf). 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](https://rajpurkar.github.io/SQuAD-explorer/) dataset.
|
||||
|
||||
|
||||
## Details of the downstream task (Q&A) - Dataset 📚
|
||||
|
||||
**SQuAD2.0** 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.
|
||||
|
||||
## Model training 🏋️
|
||||
|
||||
The model was trained on a Tesla P100 GPU and 25GB of RAM with the following command:
|
||||
|
||||
```bash
|
||||
python transformers/examples/question-answering/run_squad.py \
|
||||
--model_type electra \
|
||||
--model_name_or_path 'google/electra-small-discriminator' \
|
||||
--do_eval \
|
||||
--do_train \
|
||||
--do_lower_case \
|
||||
--train_file '/content/dataset/train-v2.0.json' \
|
||||
--predict_file '/content/dataset/dev-v2.0.json' \
|
||||
--per_gpu_train_batch_size 16 \
|
||||
--learning_rate 3e-5 \
|
||||
--num_train_epochs 10 \
|
||||
--max_seq_length 384 \
|
||||
--doc_stride 128 \
|
||||
--output_dir '/content/output' \
|
||||
--overwrite_output_dir \
|
||||
--save_steps 1000 \
|
||||
--version_2_with_negative
|
||||
```
|
||||
|
||||
## Test set Results 🧾
|
||||
|
||||
| Metric | # Value |
|
||||
| ------ | --------- |
|
||||
| **EM** | **69.71** |
|
||||
| **F1** | **73.44** |
|
||||
| **Size**| **50 MB** |
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
'exact': 69.71279373368147,
|
||||
'f1': 73.4439546123672,
|
||||
'total': 11873,
|
||||
'HasAns_exact': 69.92240215924427,
|
||||
'HasAns_f1': 77.39542393937836,
|
||||
'HasAns_total': 5928,
|
||||
'NoAns_exact': 69.50378469301934,
|
||||
'NoAns_f1': 69.50378469301934,
|
||||
'NoAns_total': 5945,
|
||||
'best_exact': 69.71279373368147,
|
||||
'best_exact_thresh': 0.0,
|
||||
'best_f1': 73.44395461236732,
|
||||
'best_f1_thresh': 0.0
|
||||
}
|
||||
```
|
||||
|
||||
### Model in action 🚀
|
||||
|
||||
Fast usage with **pipelines**:
|
||||
|
||||
```python
|
||||
from transformers import pipeline
|
||||
QnA_pipeline = pipeline('question-answering', model='mrm8488/electra-base-finetuned-squadv2')
|
||||
QnA_pipeline({
|
||||
'context': 'A new strain of flu that has the potential to become a pandemic has been identified in China by scientists.',
|
||||
'question': 'What has been discovered by scientists from China ?'
|
||||
})
|
||||
# Output:
|
||||
{'answer': 'A new strain of flu', 'end': 19, 'score': 0.8650811568752914, 'start': 0}
|
||||
```
|
||||
|
||||
> 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,102 @@
|
||||
---
|
||||
language: spanish
|
||||
thumbnail: https://imgur.com/uxAvBfh
|
||||
---
|
||||
|
||||
# Electricidad small + Spanish SQuAD v1 ⚡❓
|
||||
|
||||
[Electricidad-small-discriminator](https://huggingface.co/mrm8488/electricidad-small-discriminator) fine-tuned on [Spanish SQUAD v1.1 dataset](https://github.com/ccasimiro88/TranslateAlignRetrieve/tree/master/SQuAD-es-v1.1) for **Q&A** downstream task.
|
||||
|
||||
## Details of the downstream task (Q&A) - Dataset 📚
|
||||
|
||||
[SQuAD-es-v1.1](https://github.com/ccasimiro88/TranslateAlignRetrieve/tree/master/SQuAD-es-v1.1)
|
||||
|
||||
| Dataset split | # Samples |
|
||||
| ------------- | --------- |
|
||||
| Train | 130 K |
|
||||
| Test | 11 K |
|
||||
|
||||
## Model training 🏋️
|
||||
|
||||
The model was trained on a Tesla P100 GPU and 25GB of RAM with the following command:
|
||||
|
||||
```bash
|
||||
python /content/transformers/examples/question-answering/run_squad.py \
|
||||
--model_type electra \
|
||||
--model_name_or_path 'mrm8488/electricidad-small-discriminator' \
|
||||
--do_eval \
|
||||
--do_train \
|
||||
--do_lower_case \
|
||||
--train_file '/content/dataset/train-v1.1-es.json' \
|
||||
--predict_file '/content/dataset/dev-v1.1-es.json' \
|
||||
--per_gpu_train_batch_size 16 \
|
||||
--learning_rate 3e-5 \
|
||||
--num_train_epochs 10 \
|
||||
--max_seq_length 384 \
|
||||
--doc_stride 128 \
|
||||
--output_dir '/content/electricidad-small-finetuned-squadv1-es' \
|
||||
--overwrite_output_dir \
|
||||
--save_steps 1000
|
||||
```
|
||||
|
||||
## Test set Results 🧾
|
||||
|
||||
| Metric | # Value |
|
||||
| ------ | --------- |
|
||||
| **EM** | **46.82** |
|
||||
| **F1** | **64.79** |
|
||||
|
||||
```json
|
||||
{
|
||||
'exact': 46.82119205298013,
|
||||
'f1': 64.79435260021918,
|
||||
'total': 10570,
|
||||
'HasAns_exact': 46.82119205298013,
|
||||
HasAns_f1': 64.79435260021918,
|
||||
'HasAns_total': 10570,
|
||||
'best_exact': 46.82119205298013,
|
||||
'best_exact_thresh': 0.0,
|
||||
'best_f1': 64.79435260021918,
|
||||
'best_f1_thresh': 0.0
|
||||
}
|
||||
```
|
||||
|
||||
### Model in action 🚀
|
||||
|
||||
Fast usage with **pipelines**:
|
||||
|
||||
```python
|
||||
from transformers import pipeline
|
||||
|
||||
qa_pipeline = pipeline(
|
||||
"question-answering",
|
||||
model="mrm8488/electricidad-small-finetuned-squadv1-es",
|
||||
tokenizer="mrm8488/electricidad-small-finetuned-squadv1-es"
|
||||
)
|
||||
|
||||
context = "Manuel ha creado una versión del modelo Electra small en español que alcanza una puntuación F1 de 65 en el dataset SQUAD-es y sólo pesa 50 MB"
|
||||
|
||||
q1 = "Cuál es su marcador F1?"
|
||||
q2 = "¿Cuál es el tamaño del modelo?"
|
||||
q3 = "¿Quién lo ha creado?"
|
||||
q4 = "¿Que es lo que ha hecho Manuel?"
|
||||
|
||||
|
||||
questions = [q1, q2, q3, q4]
|
||||
|
||||
for question in questions:
|
||||
result = qa_pipeline({
|
||||
'context': context,
|
||||
'question': question})
|
||||
print(result)
|
||||
|
||||
# Output:
|
||||
{'score': 0.14836778166355025, 'start': 98, 'end': 100, 'answer': '65'}
|
||||
{'score': 0.32219420810758237, 'start': 136, 'end': 140, 'answer': '50 MB'}
|
||||
{'score': 0.9672326951118713, 'start': 0, 'end': 6, 'answer': 'Manuel'}
|
||||
{'score': 0.23552458113848118, 'start': 10, 'end': 53, 'answer': 'creado una versión del modelo Electra small'}
|
||||
```
|
||||
|
||||
> 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
|
||||
@@ -71,11 +71,15 @@ extras["sklearn"] = ["scikit-learn"]
|
||||
# keras2onnx and onnxconverter-common version is specific through a commit until 1.7.0 lands on pypi
|
||||
extras["tf"] = [
|
||||
"tensorflow",
|
||||
# "onnxconverter-common",
|
||||
# "keras2onnx"
|
||||
"onnxconverter-common @ git+git://github.com/microsoft/onnxconverter-common.git@f64ca15989b6dc95a1f3507ff6e4c395ba12dff5#egg=onnxconverter-common",
|
||||
"keras2onnx @ git+git://github.com/onnx/keras-onnx.git@cbdc75cb950b16db7f0a67be96a278f8d2953b48#egg=keras2onnx"
|
||||
]
|
||||
extras["tf-cpu"] = [
|
||||
"tensorflow-cpu",
|
||||
# "onnxconverter-common",
|
||||
# "keras2onnx"
|
||||
"onnxconverter-common @ git+git://github.com/microsoft/onnxconverter-common.git@f64ca15989b6dc95a1f3507ff6e4c395ba12dff5#egg=onnxconverter-common",
|
||||
"keras2onnx @ git+git://github.com/onnx/keras-onnx.git@cbdc75cb950b16db7f0a67be96a278f8d2953b48#egg=keras2onnx"
|
||||
]
|
||||
@@ -89,6 +93,7 @@ extras["testing"] = ["pytest", "pytest-xdist", "timeout-decorator", "psutil"]
|
||||
extras["docs"] = ["recommonmark", "sphinx", "sphinx-markdown-tables", "sphinx-rtd-theme==0.4.3", "sphinx-copybutton"]
|
||||
extras["quality"] = [
|
||||
"black",
|
||||
# "isort",
|
||||
"isort @ git+git://github.com/timothycrosley/isort.git@e63ae06ec7d70b06df9e528357650281a3d3ec22#egg=isort",
|
||||
"flake8",
|
||||
]
|
||||
@@ -96,7 +101,7 @@ extras["dev"] = extras["testing"] + extras["quality"] + ["mecab-python3<1", "sci
|
||||
|
||||
setup(
|
||||
name="transformers",
|
||||
version="3.0.0",
|
||||
version="3.0.1",
|
||||
author="Thomas Wolf, Lysandre Debut, Victor Sanh, Julien Chaumond, Sam Shleifer, Patrick von Platen, Google AI Language Team Authors, Open AI team Authors, Facebook AI Authors, Carnegie Mellon University Authors",
|
||||
author_email="thomas@huggingface.co",
|
||||
description="State-of-the-art Natural Language Processing for TensorFlow 2.0 and PyTorch",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# 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.
|
||||
|
||||
__version__ = "3.0.0"
|
||||
__version__ = "3.0.1"
|
||||
|
||||
# Work around to update TensorFlow's absl.logging threshold which alters the
|
||||
# default Python logging output behavior when present.
|
||||
|
||||
@@ -347,6 +347,10 @@ class TFGenerationMixin:
|
||||
encoder_outputs = None
|
||||
cur_len = shape_list(input_ids)[-1]
|
||||
|
||||
assert (
|
||||
cur_len < max_length
|
||||
), f"The context has {cur_len} number of tokens, but `max_length` is only {max_length}. Please make sure that `max_length` is bigger than the number of tokens, by setting either `generate(max_length=...,...)` or `config.max_length = ...`"
|
||||
|
||||
if num_beams > 1:
|
||||
output = self._generate_beam_search(
|
||||
input_ids,
|
||||
|
||||
@@ -428,6 +428,10 @@ class GenerationMixin:
|
||||
encoder_outputs = None
|
||||
cur_len = input_ids.shape[-1]
|
||||
|
||||
assert (
|
||||
cur_len < max_length
|
||||
), f"The context has {cur_len} number of tokens, but `max_length` is only {max_length}. Please make sure that `max_length` is bigger than the number of tokens, by setting either `generate(max_length=...,...)` or `config.max_length = ...`"
|
||||
|
||||
if num_beams > 1:
|
||||
output = self._generate_beam_search(
|
||||
input_ids,
|
||||
|
||||
@@ -141,7 +141,6 @@ logger = logging.getLogger(__name__)
|
||||
TF_MODEL_MAPPING = OrderedDict(
|
||||
[
|
||||
(AlbertConfig, TFAlbertModel),
|
||||
(BertConfig, TFBertModel),
|
||||
(CamembertConfig, TFCamembertModel),
|
||||
(CTRLConfig, TFCTRLModel),
|
||||
(DistilBertConfig, TFDistilBertModel),
|
||||
@@ -151,6 +150,7 @@ TF_MODEL_MAPPING = OrderedDict(
|
||||
(MobileBertConfig, TFMobileBertModel),
|
||||
(OpenAIGPTConfig, TFOpenAIGPTModel),
|
||||
(RobertaConfig, TFRobertaModel),
|
||||
(BertConfig, TFBertModel),
|
||||
(T5Config, TFT5Model),
|
||||
(TransfoXLConfig, TFTransfoXLModel),
|
||||
(XLMConfig, TFXLMModel),
|
||||
@@ -162,7 +162,6 @@ TF_MODEL_MAPPING = OrderedDict(
|
||||
TF_MODEL_FOR_PRETRAINING_MAPPING = OrderedDict(
|
||||
[
|
||||
(AlbertConfig, TFAlbertForPreTraining),
|
||||
(BertConfig, TFBertForPreTraining),
|
||||
(CamembertConfig, TFCamembertForMaskedLM),
|
||||
(CTRLConfig, TFCTRLLMHeadModel),
|
||||
(DistilBertConfig, TFDistilBertForMaskedLM),
|
||||
@@ -172,6 +171,7 @@ TF_MODEL_FOR_PRETRAINING_MAPPING = OrderedDict(
|
||||
(MobileBertConfig, TFMobileBertForPreTraining),
|
||||
(OpenAIGPTConfig, TFOpenAIGPTLMHeadModel),
|
||||
(RobertaConfig, TFRobertaForMaskedLM),
|
||||
(BertConfig, TFBertForPreTraining),
|
||||
(T5Config, TFT5ForConditionalGeneration),
|
||||
(TransfoXLConfig, TFTransfoXLLMHeadModel),
|
||||
(XLMConfig, TFXLMWithLMHeadModel),
|
||||
@@ -183,7 +183,6 @@ TF_MODEL_FOR_PRETRAINING_MAPPING = OrderedDict(
|
||||
TF_MODEL_WITH_LM_HEAD_MAPPING = OrderedDict(
|
||||
[
|
||||
(AlbertConfig, TFAlbertForMaskedLM),
|
||||
(BertConfig, TFBertForMaskedLM),
|
||||
(CamembertConfig, TFCamembertForMaskedLM),
|
||||
(CTRLConfig, TFCTRLLMHeadModel),
|
||||
(DistilBertConfig, TFDistilBertForMaskedLM),
|
||||
@@ -193,6 +192,7 @@ TF_MODEL_WITH_LM_HEAD_MAPPING = OrderedDict(
|
||||
(MobileBertConfig, TFMobileBertForMaskedLM),
|
||||
(OpenAIGPTConfig, TFOpenAIGPTLMHeadModel),
|
||||
(RobertaConfig, TFRobertaForMaskedLM),
|
||||
(BertConfig, TFBertForMaskedLM),
|
||||
(T5Config, TFT5ForConditionalGeneration),
|
||||
(TransfoXLConfig, TFTransfoXLLMHeadModel),
|
||||
(XLMConfig, TFXLMWithLMHeadModel),
|
||||
@@ -204,12 +204,12 @@ TF_MODEL_WITH_LM_HEAD_MAPPING = OrderedDict(
|
||||
TF_MODEL_FOR_MULTIPLE_CHOICE_MAPPING = OrderedDict(
|
||||
[
|
||||
(AlbertConfig, TFAlbertForMultipleChoice),
|
||||
(BertConfig, TFBertForMultipleChoice),
|
||||
(CamembertConfig, TFCamembertForMultipleChoice),
|
||||
(DistilBertConfig, TFDistilBertForMultipleChoice),
|
||||
(FlaubertConfig, TFFlaubertForMultipleChoice),
|
||||
(MobileBertConfig, TFMobileBertForMultipleChoice),
|
||||
(RobertaConfig, TFRobertaForMultipleChoice),
|
||||
(BertConfig, TFBertForMultipleChoice),
|
||||
(XLMConfig, TFXLMForMultipleChoice),
|
||||
(XLMRobertaConfig, TFXLMRobertaForMultipleChoice),
|
||||
(XLNetConfig, TFXLNetForMultipleChoice),
|
||||
@@ -219,13 +219,13 @@ TF_MODEL_FOR_MULTIPLE_CHOICE_MAPPING = OrderedDict(
|
||||
TF_MODEL_FOR_QUESTION_ANSWERING_MAPPING = OrderedDict(
|
||||
[
|
||||
(AlbertConfig, TFAlbertForQuestionAnswering),
|
||||
(BertConfig, TFBertForQuestionAnswering),
|
||||
(CamembertConfig, TFCamembertForQuestionAnswering),
|
||||
(DistilBertConfig, TFDistilBertForQuestionAnswering),
|
||||
(ElectraConfig, TFElectraForQuestionAnswering),
|
||||
(FlaubertConfig, TFFlaubertForQuestionAnsweringSimple),
|
||||
(MobileBertConfig, TFMobileBertForQuestionAnswering),
|
||||
(RobertaConfig, TFRobertaForQuestionAnswering),
|
||||
(BertConfig, TFBertForQuestionAnswering),
|
||||
(XLMConfig, TFXLMForQuestionAnsweringSimple),
|
||||
(XLMRobertaConfig, TFXLMRobertaForQuestionAnswering),
|
||||
(XLNetConfig, TFXLNetForQuestionAnsweringSimple),
|
||||
@@ -235,12 +235,12 @@ TF_MODEL_FOR_QUESTION_ANSWERING_MAPPING = OrderedDict(
|
||||
TF_MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING = OrderedDict(
|
||||
[
|
||||
(AlbertConfig, TFAlbertForSequenceClassification),
|
||||
(BertConfig, TFBertForSequenceClassification),
|
||||
(CamembertConfig, TFCamembertForSequenceClassification),
|
||||
(DistilBertConfig, TFDistilBertForSequenceClassification),
|
||||
(FlaubertConfig, TFFlaubertForSequenceClassification),
|
||||
(MobileBertConfig, TFMobileBertForSequenceClassification),
|
||||
(RobertaConfig, TFRobertaForSequenceClassification),
|
||||
(BertConfig, TFBertForSequenceClassification),
|
||||
(XLMConfig, TFXLMForSequenceClassification),
|
||||
(XLMRobertaConfig, TFXLMRobertaForSequenceClassification),
|
||||
(XLNetConfig, TFXLNetForSequenceClassification),
|
||||
@@ -250,13 +250,13 @@ TF_MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING = OrderedDict(
|
||||
TF_MODEL_FOR_TOKEN_CLASSIFICATION_MAPPING = OrderedDict(
|
||||
[
|
||||
(AlbertConfig, TFAlbertForTokenClassification),
|
||||
(BertConfig, TFBertForTokenClassification),
|
||||
(CamembertConfig, TFCamembertForTokenClassification),
|
||||
(DistilBertConfig, TFDistilBertForTokenClassification),
|
||||
(ElectraConfig, TFElectraForTokenClassification),
|
||||
(FlaubertConfig, TFFlaubertForTokenClassification),
|
||||
(MobileBertConfig, TFMobileBertForTokenClassification),
|
||||
(RobertaConfig, TFRobertaForTokenClassification),
|
||||
(BertConfig, TFBertForTokenClassification),
|
||||
(XLMConfig, TFXLMForTokenClassification),
|
||||
(XLMRobertaConfig, TFXLMRobertaForTokenClassification),
|
||||
(XLNetConfig, TFXLNetForTokenClassification),
|
||||
|
||||
@@ -478,7 +478,7 @@ class TFT5Block(tf.keras.layers.Layer):
|
||||
return outputs # hidden-states, present_key_value_states, (self-attention weights), (self-attention position bias), (cross-attention weights), (cross-attention position bias)
|
||||
|
||||
|
||||
class _NoLayerEmbedTokens(object):
|
||||
class _NoLayerEmbedTokens:
|
||||
"""
|
||||
this class wraps a the TFSharedEmbeddingTokens layer into a python 'no-keras-layer'
|
||||
class to avoid problem with weight restoring. Also it makes sure that the layer is
|
||||
@@ -655,7 +655,7 @@ class TFT5MainLayer(tf.keras.layers.Layer):
|
||||
# Since we are adding it to the raw scores before the softmax, this is
|
||||
# effectively the same as removing these entirely.
|
||||
|
||||
# T5 has a mask that can compare sequence ids, we can simulate this here with this transposistion
|
||||
# T5 has a mask that can compare sequence ids, we can simulate this here with this transposition
|
||||
# Cf. https://github.com/tensorflow/mesh/blob/8d2465e9bc93129b913b5ccc6a59aa97abd96ec6/mesh_tensorflow/transformer/transformer_layers.py#L270
|
||||
# extended_attention_mask = tf.math.equal(extended_attention_mask,
|
||||
# tf.transpose(extended_attention_mask, perm=(-1, -2)))
|
||||
@@ -682,16 +682,8 @@ class TFT5MainLayer(tf.keras.layers.Layer):
|
||||
else:
|
||||
encoder_extended_attention_mask = None
|
||||
|
||||
# Prepare head mask if needed
|
||||
# 1.0 in head_mask indicate we keep the head
|
||||
# attention_probs has shape bsz x n_heads x N x N
|
||||
# input head_mask has shape [num_heads] or [num_hidden_layers x num_heads]
|
||||
# and head_mask is converted to shape [num_hidden_layers x batch x num_heads x seq_length x seq_length]
|
||||
if head_mask is not None:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
head_mask = [None] * self.num_hidden_layers
|
||||
# head_mask = tf.constant([0] * self.num_hidden_layers)
|
||||
assert head_mask is None, "Head mask not supported"
|
||||
head_mask = [None] * self.num_hidden_layers
|
||||
|
||||
present_key_value_states = ()
|
||||
all_hidden_states = ()
|
||||
@@ -1054,8 +1046,6 @@ class TFT5ForConditionalGeneration(TFT5PreTrainedModel):
|
||||
r"""
|
||||
Returns:
|
||||
:obj:`tuple(tf.Tensor)` comprising various elements depending on the configuration (:class:`~transformers.T5Config`) and inputs:
|
||||
loss (:obj:`tf.Tensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`lm_label` is provided):
|
||||
Classification loss (cross entropy).
|
||||
prediction_scores (:obj:`tf.Tensor` of shape :obj:`(batch_size, sequence_length, config.vocab_size)`)
|
||||
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
|
||||
decoder_past_key_value_states (:obj:`tuple(tuple(tf.Tensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`, `optional`, returned when ``use_cache=True``):
|
||||
|
||||
@@ -1272,32 +1272,33 @@ class QuestionAnsweringPipeline(Pipeline):
|
||||
with self.device_placement():
|
||||
if self.framework == "tf":
|
||||
fw_args = {k: tf.constant(v) for (k, v) in fw_args.items()}
|
||||
start, end = self.model(fw_args)
|
||||
start, end = self.model(fw_args)[:2]
|
||||
start, end = start.numpy(), end.numpy()
|
||||
else:
|
||||
with torch.no_grad():
|
||||
# Retrieve the score for the context tokens only (removing question tokens)
|
||||
fw_args = {k: torch.tensor(v, device=self.device) for (k, v) in fw_args.items()}
|
||||
start, end = self.model(**fw_args)
|
||||
start, end = self.model(**fw_args)[:2]
|
||||
start, end = start.cpu().numpy(), end.cpu().numpy()
|
||||
|
||||
min_null_score = 1000000 # large and positive
|
||||
answers = []
|
||||
for (feature, start_, end_) in zip(features, start, end):
|
||||
# Normalize logits and spans to retrieve the answer
|
||||
start_ = np.exp(start_) / np.sum(np.exp(start_))
|
||||
end_ = np.exp(end_) / np.sum(np.exp(end_))
|
||||
|
||||
# Mask padding and question
|
||||
start_, end_ = (
|
||||
start_ * np.abs(np.array(feature.p_mask) - 1),
|
||||
end_ * np.abs(np.array(feature.p_mask) - 1),
|
||||
)
|
||||
|
||||
# Normalize logits and spans to retrieve the answer
|
||||
start_ = np.exp(start_ - np.log(np.sum(np.exp(start_), axis=-1, keepdims=True)))
|
||||
end_ = np.exp(end_ - np.log(np.sum(np.exp(end_), axis=-1, keepdims=True)))
|
||||
|
||||
if kwargs["handle_impossible_answer"]:
|
||||
min_null_score = min(min_null_score, (start_[0] * end_[0]).item())
|
||||
|
||||
start_[0] = end_[0] = 0
|
||||
# Mask the CLS tokens after softmax & handling of impossible answer
|
||||
start_[0] = end_[0]
|
||||
|
||||
starts, ends, scores = self.decode(start_, end_, kwargs["topk"], kwargs["max_answer_len"])
|
||||
char_to_word = np.array(example.char_to_word_offset)
|
||||
|
||||
@@ -454,12 +454,12 @@ class PreTrainedTokenizer(PreTrainedTokenizerBase):
|
||||
first_ids = get_input_ids(text)
|
||||
second_ids = get_input_ids(text_pair) if text_pair is not None else None
|
||||
|
||||
return self._prepare_for_model(
|
||||
return self.prepare_for_model(
|
||||
first_ids,
|
||||
pair_ids=second_ids,
|
||||
add_special_tokens=add_special_tokens,
|
||||
padding_strategy=padding_strategy,
|
||||
truncation_strategy=truncation_strategy,
|
||||
padding=padding_strategy.value,
|
||||
truncation=truncation_strategy.value,
|
||||
max_length=max_length,
|
||||
stride=stride,
|
||||
pad_to_multiple_of=pad_to_multiple_of,
|
||||
@@ -584,7 +584,7 @@ class PreTrainedTokenizer(PreTrainedTokenizerBase):
|
||||
|
||||
batch_outputs = {}
|
||||
for first_ids, second_ids in batch_ids_pairs:
|
||||
outputs = self._prepare_for_model(
|
||||
outputs = self.prepare_for_model(
|
||||
first_ids,
|
||||
second_ids,
|
||||
add_special_tokens=add_special_tokens,
|
||||
@@ -620,109 +620,6 @@ class PreTrainedTokenizer(PreTrainedTokenizerBase):
|
||||
|
||||
return batch_outputs
|
||||
|
||||
@add_end_docstrings(ENCODE_KWARGS_DOCSTRING, ENCODE_PLUS_ADDITIONAL_KWARGS_DOCSTRING)
|
||||
def _prepare_for_model(
|
||||
self,
|
||||
ids: List[int],
|
||||
pair_ids: Optional[List[int]] = None,
|
||||
add_special_tokens: bool = True,
|
||||
padding_strategy: PaddingStrategy = PaddingStrategy.DO_NOT_PAD,
|
||||
truncation_strategy: TruncationStrategy = TruncationStrategy.DO_NOT_TRUNCATE,
|
||||
max_length: Optional[int] = None,
|
||||
stride: int = 0,
|
||||
pad_to_multiple_of: Optional[int] = None,
|
||||
return_tensors: Optional[str] = None,
|
||||
prepend_batch_axis: bool = False,
|
||||
return_token_type_ids: Optional[bool] = None,
|
||||
return_attention_mask: Optional[bool] = None,
|
||||
return_overflowing_tokens: bool = False,
|
||||
return_special_tokens_mask: bool = False,
|
||||
return_length: bool = False,
|
||||
verbose: bool = True,
|
||||
) -> BatchEncoding:
|
||||
""" Prepares a sequence of input id, or a pair of sequences of inputs ids so that it can be used by the model.
|
||||
It adds special tokens, truncates sequences if overflowing while taking into account the special tokens and
|
||||
manages a moving window (with user defined stride) for overflowing tokens
|
||||
|
||||
Args:
|
||||
ids: list of tokenized input ids. Can be obtained from a string by chaining the
|
||||
`tokenize` and `convert_tokens_to_ids` methods.
|
||||
pair_ids: Optional second list of input ids. Can be obtained from a string by chaining the
|
||||
`tokenize` and `convert_tokens_to_ids` methods.
|
||||
"""
|
||||
pair = bool(pair_ids is not None)
|
||||
len_ids = len(ids)
|
||||
len_pair_ids = len(pair_ids) if pair else 0
|
||||
|
||||
# Load from model defaults
|
||||
if return_token_type_ids is None:
|
||||
return_token_type_ids = "token_type_ids" in self.model_input_names
|
||||
if return_attention_mask is None:
|
||||
return_attention_mask = "attention_mask" in self.model_input_names
|
||||
|
||||
encoded_inputs = {}
|
||||
|
||||
# Compute the total size of the returned encodings
|
||||
total_len = len_ids + len_pair_ids + (self.num_special_tokens_to_add(pair=pair) if add_special_tokens else 0)
|
||||
|
||||
# Truncation: Handle max sequence length
|
||||
if truncation_strategy != TruncationStrategy.DO_NOT_TRUNCATE and max_length and total_len > max_length:
|
||||
ids, pair_ids, overflowing_tokens = self.truncate_sequences(
|
||||
ids,
|
||||
pair_ids=pair_ids,
|
||||
num_tokens_to_remove=total_len - max_length,
|
||||
truncation_strategy=truncation_strategy,
|
||||
stride=stride,
|
||||
)
|
||||
if return_overflowing_tokens:
|
||||
encoded_inputs["overflowing_tokens"] = overflowing_tokens
|
||||
encoded_inputs["num_truncated_tokens"] = total_len - max_length
|
||||
|
||||
# Add special tokens
|
||||
if add_special_tokens:
|
||||
sequence = self.build_inputs_with_special_tokens(ids, pair_ids)
|
||||
token_type_ids = self.create_token_type_ids_from_sequences(ids, pair_ids)
|
||||
else:
|
||||
sequence = ids + pair_ids if pair else ids
|
||||
token_type_ids = [0] * len(ids) + ([1] * len(pair_ids) if pair else [])
|
||||
|
||||
# Build output dictionnary
|
||||
encoded_inputs["input_ids"] = sequence
|
||||
if return_token_type_ids:
|
||||
encoded_inputs["token_type_ids"] = token_type_ids
|
||||
if return_special_tokens_mask:
|
||||
if add_special_tokens:
|
||||
encoded_inputs["special_tokens_mask"] = self.get_special_tokens_mask(ids, pair_ids)
|
||||
else:
|
||||
encoded_inputs["special_tokens_mask"] = [0] * len(sequence)
|
||||
|
||||
# Check lengths
|
||||
if max_length is None and len(encoded_inputs["input_ids"]) > self.model_max_length and verbose:
|
||||
logger.warning(
|
||||
"Token indices sequence length is longer than the specified maximum sequence length "
|
||||
"for this model ({} > {}). Running this sequence through the model will result in "
|
||||
"indexing errors".format(len(ids), self.model_max_length)
|
||||
)
|
||||
|
||||
# Padding
|
||||
if padding_strategy != PaddingStrategy.DO_NOT_PAD or return_attention_mask:
|
||||
encoded_inputs = self.pad(
|
||||
encoded_inputs,
|
||||
max_length=max_length,
|
||||
padding=padding_strategy.value,
|
||||
pad_to_multiple_of=pad_to_multiple_of,
|
||||
return_attention_mask=return_attention_mask,
|
||||
)
|
||||
|
||||
if return_length:
|
||||
encoded_inputs["length"] = len(encoded_inputs["input_ids"])
|
||||
|
||||
batch_outputs = BatchEncoding(
|
||||
encoded_inputs, tensor_type=return_tensors, prepend_batch_axis=prepend_batch_axis
|
||||
)
|
||||
|
||||
return batch_outputs
|
||||
|
||||
def prepare_for_tokenization(self, text: str, is_pretokenized=False, **kwargs) -> (str, dict):
|
||||
""" Performs any necessary transformations before tokenization.
|
||||
|
||||
@@ -731,90 +628,6 @@ class PreTrainedTokenizer(PreTrainedTokenizerBase):
|
||||
"""
|
||||
return (text, kwargs)
|
||||
|
||||
def truncate_sequences(
|
||||
self,
|
||||
ids: List[int],
|
||||
pair_ids: Optional[List[int]] = None,
|
||||
num_tokens_to_remove: int = 0,
|
||||
truncation_strategy: Union[str, TruncationStrategy] = "only_first",
|
||||
stride: int = 0,
|
||||
) -> Tuple[List[int], List[int], List[int]]:
|
||||
""" Truncates a sequence pair in place to the maximum length.
|
||||
|
||||
Args:
|
||||
ids: list of tokenized input ids. Can be obtained from a string by chaining the
|
||||
`tokenize` and `convert_tokens_to_ids` methods.
|
||||
pair_ids: Optional second list of input ids. Can be obtained from a string by chaining the
|
||||
`tokenize` and `convert_tokens_to_ids` methods.
|
||||
num_tokens_to_remove (:obj:`int`, `optional`, defaults to ``0``):
|
||||
number of tokens to remove using the truncation strategy
|
||||
truncation_strategy (:obj:`string`, `optional`, defaults to "only_first"):
|
||||
String selected in the following options:
|
||||
|
||||
- 'only_first' (default): Only truncate the first sequence. raise an error if the first sequence is shorter or equal to than num_tokens_to_remove.
|
||||
- 'only_second': Only truncate the second sequence
|
||||
- 'longest_first': Iteratively reduce the inputs sequence until the input is under max_length
|
||||
starting from the longest one at each token (when there is a pair of input sequences).
|
||||
Overflowing tokens only contains overflow from the first sequence.
|
||||
- 'do_not_truncate'
|
||||
stride (:obj:`int`, `optional`, defaults to ``0``):
|
||||
If set to a number along with max_length, the overflowing tokens returned will contain some tokens
|
||||
from the main sequence returned. The value of this argument defines the number of additional tokens.
|
||||
"""
|
||||
if num_tokens_to_remove <= 0:
|
||||
return ids, pair_ids, []
|
||||
|
||||
if not isinstance(truncation_strategy, TruncationStrategy):
|
||||
truncation_strategy = TruncationStrategy(truncation_strategy)
|
||||
|
||||
overflowing_tokens = []
|
||||
if truncation_strategy == TruncationStrategy.LONGEST_FIRST:
|
||||
for _ in range(num_tokens_to_remove):
|
||||
if pair_ids is None or len(ids) > len(pair_ids):
|
||||
ids = ids[:-1]
|
||||
else:
|
||||
pair_ids = pair_ids[:-1]
|
||||
elif truncation_strategy == TruncationStrategy.ONLY_FIRST:
|
||||
if len(ids) > num_tokens_to_remove:
|
||||
window_len = min(len(ids), stride + num_tokens_to_remove)
|
||||
overflowing_tokens = ids[-window_len:]
|
||||
ids = ids[:-num_tokens_to_remove]
|
||||
else:
|
||||
logger.error(
|
||||
f"We need to remove {num_tokens_to_remove} to truncate the input"
|
||||
f"but the first sequence has a length {len(ids)}. "
|
||||
f"Please select another truncation strategy than {truncation_strategy}, "
|
||||
f"for instance 'longest_first' or 'only_second'."
|
||||
)
|
||||
elif truncation_strategy == TruncationStrategy.ONLY_SECOND and pair_ids is not None:
|
||||
if len(pair_ids) > num_tokens_to_remove:
|
||||
window_len = min(len(pair_ids), stride + num_tokens_to_remove)
|
||||
overflowing_tokens = pair_ids[-window_len:]
|
||||
pair_ids = pair_ids[:-num_tokens_to_remove]
|
||||
else:
|
||||
logger.error(
|
||||
f"We need to remove {num_tokens_to_remove} to truncate the input"
|
||||
f"but the second sequence has a length {len(pair_ids)}. "
|
||||
f"Please select another truncation strategy than {truncation_strategy}, "
|
||||
f"for instance 'longest_first' or 'only_first'."
|
||||
)
|
||||
|
||||
return (ids, pair_ids, overflowing_tokens)
|
||||
|
||||
def create_token_type_ids_from_sequences(self, token_ids_0: List, token_ids_1: Optional[List] = None) -> List[int]:
|
||||
if token_ids_1 is None:
|
||||
return len(token_ids_0) * [0]
|
||||
return [0] * len(token_ids_0) + [1] * len(token_ids_1)
|
||||
|
||||
def build_inputs_with_special_tokens(self, token_ids_0: List, token_ids_1: Optional[List] = None) -> List:
|
||||
"""
|
||||
Build model inputs from a sequence or a pair of sequence for sequence classification tasks
|
||||
by concatenating and adding special tokens. This implementation does not add special tokens.
|
||||
"""
|
||||
if token_ids_1 is None:
|
||||
return token_ids_0
|
||||
return token_ids_0 + token_ids_1
|
||||
|
||||
def get_special_tokens_mask(
|
||||
self, token_ids_0: List, token_ids_1: Optional[List] = None, already_has_special_tokens: bool = False
|
||||
) -> List[int]:
|
||||
|
||||
@@ -945,9 +945,9 @@ ENCODE_KWARGS_DOCSTRING = r"""
|
||||
`truncation` (:obj:`Union[bool, str]`, `optional`, defaults to :obj:`False`):
|
||||
Activate and control truncation. Accepts the following values:
|
||||
|
||||
* `True` or `'only_first'`: truncate to a max length specified in `max_length` or to the max acceptable input length for the model if no length is provided (`max_length=None`). This will only truncate the first sequence of a pair if a pair of sequences (or a batch of pairs) is provided,
|
||||
* `True` or `'longest_first'`: truncate to a max length specified in `max_length` or to the max acceptable input length for the model if no length is provided (`max_length=None`). This will truncate token by token, removing a token from the longest sequence in the pair if a pair of sequences (or a batch of pairs) is provided,
|
||||
* `'only_first'`: truncate to a max length specified in `max_length` or to the max acceptable input length for the model if no length is provided (`max_length=None`). This will only truncate the first sequence of a pair if a pair of sequences (or a batch of pairs) is provided,
|
||||
* `'only_second'`: truncate to a max length specified in `max_length` or to the max acceptable input length for the model if no length is provided (`max_length=None`). This will only truncate the second sequence of a pair if a pair of sequences (or a batch of pairs) is provided,
|
||||
* `'longest_first'`: truncate to a max length specified in `max_length` or to the max acceptable input length for the model if no length is provided (`max_length=None`). This will truncate token by token, removing a token from the longest sequence in the pair if a pair of sequences (or a batch of pairs) is provided,
|
||||
* `False` or `'do_not_truncate'` (default): No truncation (i.e. can output batch with sequences length greater than the model max admissible input size)
|
||||
`max_length` (:obj:`Union[int, None]`, `optional`, defaults to :obj:`None`):
|
||||
Control the length for padding/truncation. Accepts the following values
|
||||
@@ -1446,10 +1446,11 @@ class PreTrainedTokenizerBase(SpecialTokensMixin):
|
||||
logger.warning(
|
||||
"Truncation was not explicitely activated but `max_length` is provided a specific value, "
|
||||
"please use `truncation=True` to explicitely truncate examples to max length. "
|
||||
"Defaulting to 'only_first' truncation strategy. "
|
||||
"If you encode pairs of sequences (GLUE-style) with the tokenizer you may want to check this is the right behavior."
|
||||
"Defaulting to 'longest_first' truncation strategy. "
|
||||
"If you encode pairs of sequences (GLUE-style) with the tokenizer you can select this strategy "
|
||||
"more precisely by providing a specific strategy to `truncation`."
|
||||
)
|
||||
truncation = "only_first"
|
||||
truncation = "longest_first"
|
||||
|
||||
# Get padding strategy
|
||||
if padding is False and old_pad_to_max_length:
|
||||
@@ -1469,7 +1470,7 @@ class PreTrainedTokenizerBase(SpecialTokensMixin):
|
||||
elif padding is not False:
|
||||
if padding is True:
|
||||
padding_strategy = PaddingStrategy.LONGEST # Default to pad to the longest sequence in the batch
|
||||
else:
|
||||
elif not isinstance(padding, PaddingStrategy):
|
||||
padding_strategy = PaddingStrategy(padding)
|
||||
else:
|
||||
padding_strategy = PaddingStrategy.DO_NOT_PAD
|
||||
@@ -1492,9 +1493,9 @@ class PreTrainedTokenizerBase(SpecialTokensMixin):
|
||||
elif truncation is not False:
|
||||
if truncation is True:
|
||||
truncation_strategy = (
|
||||
TruncationStrategy.ONLY_FIRST
|
||||
) # Default to truncate the first sequences in pairs of inputs
|
||||
else:
|
||||
TruncationStrategy.LONGEST_FIRST
|
||||
) # Default to truncate the longest sequences in pairs of inputs
|
||||
elif not isinstance(truncation, TruncationStrategy):
|
||||
truncation_strategy = TruncationStrategy(truncation)
|
||||
else:
|
||||
truncation_strategy = TruncationStrategy.DO_NOT_TRUNCATE
|
||||
@@ -1960,6 +1961,225 @@ class PreTrainedTokenizerBase(SpecialTokensMixin):
|
||||
|
||||
return BatchEncoding(batch_outputs, tensor_type=return_tensors)
|
||||
|
||||
def create_token_type_ids_from_sequences(self, token_ids_0: List, token_ids_1: Optional[List] = None) -> List[int]:
|
||||
if token_ids_1 is None:
|
||||
return len(token_ids_0) * [0]
|
||||
return [0] * len(token_ids_0) + [1] * len(token_ids_1)
|
||||
|
||||
def build_inputs_with_special_tokens(self, token_ids_0: List, token_ids_1: Optional[List] = None) -> List:
|
||||
"""
|
||||
Build model inputs from a sequence or a pair of sequence for sequence classification tasks
|
||||
by concatenating and adding special tokens. This implementation does not add special tokens.
|
||||
"""
|
||||
if token_ids_1 is None:
|
||||
return token_ids_0
|
||||
return token_ids_0 + token_ids_1
|
||||
|
||||
@add_end_docstrings(ENCODE_KWARGS_DOCSTRING, ENCODE_PLUS_ADDITIONAL_KWARGS_DOCSTRING)
|
||||
def prepare_for_model(
|
||||
self,
|
||||
ids: List[int],
|
||||
pair_ids: Optional[List[int]] = None,
|
||||
add_special_tokens: bool = True,
|
||||
padding: Union[bool, str] = False,
|
||||
truncation: Union[bool, str] = False,
|
||||
max_length: Optional[int] = None,
|
||||
stride: int = 0,
|
||||
pad_to_multiple_of: Optional[int] = None,
|
||||
return_tensors: Optional[Union[str, TensorType]] = None,
|
||||
return_token_type_ids: Optional[bool] = None,
|
||||
return_attention_mask: Optional[bool] = None,
|
||||
return_overflowing_tokens: bool = False,
|
||||
return_special_tokens_mask: bool = False,
|
||||
return_offsets_mapping: bool = False,
|
||||
return_length: bool = False,
|
||||
verbose: bool = True,
|
||||
prepend_batch_axis: bool = False,
|
||||
**kwargs
|
||||
) -> BatchEncoding:
|
||||
""" Prepares a sequence of input id, or a pair of sequences of inputs ids so that it can be used by the model.
|
||||
It adds special tokens, truncates sequences if overflowing while taking into account the special tokens and
|
||||
manages a moving window (with user defined stride) for overflowing tokens
|
||||
|
||||
Args:
|
||||
ids: list of tokenized input ids. Can be obtained from a string by chaining the
|
||||
`tokenize` and `convert_tokens_to_ids` methods.
|
||||
pair_ids: Optional second list of input ids. Can be obtained from a string by chaining the
|
||||
`tokenize` and `convert_tokens_to_ids` methods.
|
||||
"""
|
||||
|
||||
if "return_lengths" in kwargs:
|
||||
if verbose:
|
||||
warnings.warn(
|
||||
"The PreTrainedTokenizerBase.prepare_for_model `return_lengths` parameter is deprecated. "
|
||||
"Please use `return_length` instead.",
|
||||
FutureWarning,
|
||||
)
|
||||
return_length = kwargs["return_lengths"]
|
||||
|
||||
# Backward compatibility for 'truncation_strategy', 'pad_to_max_length'
|
||||
padding_strategy, truncation_strategy, max_length, kwargs = self._get_padding_truncation_strategies(
|
||||
padding=padding,
|
||||
truncation=truncation,
|
||||
max_length=max_length,
|
||||
pad_to_multiple_of=pad_to_multiple_of,
|
||||
verbose=verbose,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
pair = bool(pair_ids is not None)
|
||||
len_ids = len(ids)
|
||||
len_pair_ids = len(pair_ids) if pair else 0
|
||||
|
||||
# Load from model defaults
|
||||
if return_token_type_ids is None:
|
||||
return_token_type_ids = "token_type_ids" in self.model_input_names
|
||||
if return_attention_mask is None:
|
||||
return_attention_mask = "attention_mask" in self.model_input_names
|
||||
|
||||
encoded_inputs = {}
|
||||
|
||||
# Compute the total size of the returned encodings
|
||||
total_len = len_ids + len_pair_ids + (self.num_special_tokens_to_add(pair=pair) if add_special_tokens else 0)
|
||||
|
||||
# Truncation: Handle max sequence length
|
||||
if truncation_strategy != TruncationStrategy.DO_NOT_TRUNCATE and max_length and total_len > max_length:
|
||||
ids, pair_ids, overflowing_tokens = self.truncate_sequences(
|
||||
ids,
|
||||
pair_ids=pair_ids,
|
||||
num_tokens_to_remove=total_len - max_length,
|
||||
truncation_strategy=truncation_strategy,
|
||||
stride=stride,
|
||||
)
|
||||
if return_overflowing_tokens:
|
||||
encoded_inputs["overflowing_tokens"] = overflowing_tokens
|
||||
encoded_inputs["num_truncated_tokens"] = total_len - max_length
|
||||
|
||||
# Add special tokens
|
||||
if add_special_tokens:
|
||||
sequence = self.build_inputs_with_special_tokens(ids, pair_ids)
|
||||
token_type_ids = self.create_token_type_ids_from_sequences(ids, pair_ids)
|
||||
else:
|
||||
sequence = ids + pair_ids if pair else ids
|
||||
token_type_ids = [0] * len(ids) + ([1] * len(pair_ids) if pair else [])
|
||||
|
||||
# Build output dictionnary
|
||||
encoded_inputs["input_ids"] = sequence
|
||||
if return_token_type_ids:
|
||||
encoded_inputs["token_type_ids"] = token_type_ids
|
||||
if return_special_tokens_mask:
|
||||
if add_special_tokens:
|
||||
encoded_inputs["special_tokens_mask"] = self.get_special_tokens_mask(ids, pair_ids)
|
||||
else:
|
||||
encoded_inputs["special_tokens_mask"] = [0] * len(sequence)
|
||||
|
||||
# Check lengths
|
||||
if max_length is None and len(encoded_inputs["input_ids"]) > self.model_max_length and verbose:
|
||||
logger.warning(
|
||||
"Token indices sequence length is longer than the specified maximum sequence length "
|
||||
"for this model ({} > {}). Running this sequence through the model will result in "
|
||||
"indexing errors".format(len(ids), self.model_max_length)
|
||||
)
|
||||
|
||||
# Padding
|
||||
if padding_strategy != PaddingStrategy.DO_NOT_PAD or return_attention_mask:
|
||||
encoded_inputs = self.pad(
|
||||
encoded_inputs,
|
||||
max_length=max_length,
|
||||
padding=padding_strategy.value,
|
||||
pad_to_multiple_of=pad_to_multiple_of,
|
||||
return_attention_mask=return_attention_mask,
|
||||
)
|
||||
|
||||
if return_length:
|
||||
encoded_inputs["length"] = len(encoded_inputs["input_ids"])
|
||||
|
||||
batch_outputs = BatchEncoding(
|
||||
encoded_inputs, tensor_type=return_tensors, prepend_batch_axis=prepend_batch_axis
|
||||
)
|
||||
|
||||
return batch_outputs
|
||||
|
||||
def truncate_sequences(
|
||||
self,
|
||||
ids: List[int],
|
||||
pair_ids: Optional[List[int]] = None,
|
||||
num_tokens_to_remove: int = 0,
|
||||
truncation_strategy: Union[str, TruncationStrategy] = "longest_first",
|
||||
stride: int = 0,
|
||||
) -> Tuple[List[int], List[int], List[int]]:
|
||||
""" Truncates a sequence pair in place to the maximum length.
|
||||
|
||||
Args:
|
||||
ids: list of tokenized input ids. Can be obtained from a string by chaining the
|
||||
`tokenize` and `convert_tokens_to_ids` methods.
|
||||
pair_ids: Optional second list of input ids. Can be obtained from a string by chaining the
|
||||
`tokenize` and `convert_tokens_to_ids` methods.
|
||||
num_tokens_to_remove (:obj:`int`, `optional`, defaults to ``0``):
|
||||
number of tokens to remove using the truncation strategy
|
||||
truncation_strategy (:obj:`string`, `optional`, defaults to "longest_first"):
|
||||
String selected in the following options:
|
||||
|
||||
- 'longest_first' (default): Iteratively reduce the inputs sequence until the input is under max_length
|
||||
starting from the longest one at each token (when there is a pair of input sequences).
|
||||
Overflowing tokens only contains overflow from the first sequence.
|
||||
- 'only_first': Only truncate the first sequence. raise an error if the first sequence is shorter or equal to than num_tokens_to_remove.
|
||||
- 'only_second': Only truncate the second sequence
|
||||
- 'do_not_truncate'
|
||||
stride (:obj:`int`, `optional`, defaults to ``0``):
|
||||
If set to a number along with max_length, the overflowing tokens returned will contain some tokens
|
||||
from the main sequence returned. The value of this argument defines the number of additional tokens.
|
||||
"""
|
||||
if num_tokens_to_remove <= 0:
|
||||
return ids, pair_ids, []
|
||||
|
||||
if not isinstance(truncation_strategy, TruncationStrategy):
|
||||
truncation_strategy = TruncationStrategy(truncation_strategy)
|
||||
|
||||
overflowing_tokens = []
|
||||
if truncation_strategy == TruncationStrategy.LONGEST_FIRST:
|
||||
for _ in range(num_tokens_to_remove):
|
||||
if pair_ids is None or len(ids) > len(pair_ids):
|
||||
if not overflowing_tokens:
|
||||
window_len = min(len(ids), stride + 1)
|
||||
else:
|
||||
window_len = 1
|
||||
overflowing_tokens.extend(ids[-window_len:])
|
||||
ids = ids[:-1]
|
||||
else:
|
||||
if not overflowing_tokens:
|
||||
window_len = min(len(pair_ids), stride + 1)
|
||||
else:
|
||||
window_len = 1
|
||||
overflowing_tokens.extend(pair_ids[-window_len:])
|
||||
pair_ids = pair_ids[:-1]
|
||||
elif truncation_strategy == TruncationStrategy.ONLY_FIRST:
|
||||
if len(ids) > num_tokens_to_remove:
|
||||
window_len = min(len(ids), stride + num_tokens_to_remove)
|
||||
overflowing_tokens = ids[-window_len:]
|
||||
ids = ids[:-num_tokens_to_remove]
|
||||
else:
|
||||
logger.error(
|
||||
f"We need to remove {num_tokens_to_remove} to truncate the input"
|
||||
f"but the first sequence has a length {len(ids)}. "
|
||||
f"Please select another truncation strategy than {truncation_strategy}, "
|
||||
f"for instance 'longest_first' or 'only_second'."
|
||||
)
|
||||
elif truncation_strategy == TruncationStrategy.ONLY_SECOND and pair_ids is not None:
|
||||
if len(pair_ids) > num_tokens_to_remove:
|
||||
window_len = min(len(pair_ids), stride + num_tokens_to_remove)
|
||||
overflowing_tokens = pair_ids[-window_len:]
|
||||
pair_ids = pair_ids[:-num_tokens_to_remove]
|
||||
else:
|
||||
logger.error(
|
||||
f"We need to remove {num_tokens_to_remove} to truncate the input"
|
||||
f"but the second sequence has a length {len(pair_ids)}. "
|
||||
f"Please select another truncation strategy than {truncation_strategy}, "
|
||||
f"for instance 'longest_first' or 'only_first'."
|
||||
)
|
||||
|
||||
return (ids, pair_ids, overflowing_tokens)
|
||||
|
||||
def _pad(
|
||||
self,
|
||||
encoded_inputs: Union[Dict[str, EncodedInput], BatchEncoding],
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -508,9 +508,7 @@ class TokenizerTesterMixin:
|
||||
self.assertEqual(len(truncated_sequence), total_length - 2)
|
||||
self.assertEqual(truncated_sequence, sequence[:-2])
|
||||
|
||||
self.assertEqual(
|
||||
len(overflowing_tokens), 0
|
||||
) # No overflowing tokens when using 'longest' in python tokenizers
|
||||
self.assertEqual(len(overflowing_tokens), 2 + stride)
|
||||
|
||||
def test_maximum_encoding_length_pair_input(self):
|
||||
tokenizers = self.get_tokenizers(do_lower_case=False, model_max_length=100)
|
||||
@@ -634,7 +632,39 @@ class TokenizerTesterMixin:
|
||||
self.assertEqual(truncated_sequence, truncated_longest_sequence)
|
||||
|
||||
self.assertEqual(
|
||||
len(overflowing_tokens), 0
|
||||
len(overflowing_tokens), 2 + stride
|
||||
) # No overflowing tokens when using 'longest' in python tokenizers
|
||||
|
||||
information = tokenizer.encode_plus(
|
||||
seq_0,
|
||||
seq_1,
|
||||
max_length=len(sequence) - 2,
|
||||
add_special_tokens=False,
|
||||
stride=stride,
|
||||
truncation=True,
|
||||
return_overflowing_tokens=True,
|
||||
# add_prefix_space=False,
|
||||
)
|
||||
# Overflowing tokens are handled quite differently in slow and fast tokenizers
|
||||
if isinstance(tokenizer, PreTrainedTokenizerFast):
|
||||
truncated_sequence = information["input_ids"][0]
|
||||
overflowing_tokens = information["input_ids"][1]
|
||||
self.assertEqual(len(information["input_ids"]), 2)
|
||||
|
||||
self.assertEqual(len(truncated_sequence), len(sequence) - 2)
|
||||
self.assertEqual(truncated_sequence, truncated_longest_sequence)
|
||||
|
||||
self.assertEqual(len(overflowing_tokens), 2 + stride + len(smallest))
|
||||
self.assertEqual(overflowing_tokens, overflow_longest_sequence)
|
||||
else:
|
||||
truncated_sequence = information["input_ids"]
|
||||
overflowing_tokens = information["overflowing_tokens"]
|
||||
|
||||
self.assertEqual(len(truncated_sequence), len(sequence) - 2)
|
||||
self.assertEqual(truncated_sequence, truncated_longest_sequence)
|
||||
|
||||
self.assertEqual(
|
||||
len(overflowing_tokens), 2 + stride
|
||||
) # No overflowing tokens when using 'longest' in python tokenizers
|
||||
|
||||
information_first_truncated = tokenizer.encode_plus(
|
||||
@@ -643,7 +673,7 @@ class TokenizerTesterMixin:
|
||||
max_length=len(sequence) - 2,
|
||||
add_special_tokens=False,
|
||||
stride=stride,
|
||||
truncation=True,
|
||||
truncation="only_first",
|
||||
return_overflowing_tokens=True,
|
||||
# add_prefix_space=False,
|
||||
)
|
||||
@@ -1293,6 +1323,16 @@ class TokenizerTesterMixin:
|
||||
for key in output.keys():
|
||||
self.assertEqual(output[key], output_sequence[key])
|
||||
|
||||
def test_prepare_for_model(self):
|
||||
tokenizers = self.get_tokenizers(do_lower_case=False)
|
||||
for tokenizer in tokenizers:
|
||||
string_sequence = "Testing the prepare_for_model method."
|
||||
ids = tokenizer.encode(string_sequence, add_special_tokens=False)
|
||||
input_dict = tokenizer.encode_plus(string_sequence)
|
||||
prepared_input_dict = tokenizer.prepare_for_model(ids)
|
||||
|
||||
self.assertEqual(input_dict, prepared_input_dict)
|
||||
|
||||
@require_torch
|
||||
@require_tf
|
||||
def test_batch_encode_plus_tensors(self):
|
||||
|
||||
@@ -90,6 +90,7 @@ class CommonFastTokenizerTest(unittest.TestCase):
|
||||
self.assert_embeded_special_tokens(tokenizer_r, tokenizer_p)
|
||||
self.assert_padding(tokenizer_r, tokenizer_p)
|
||||
self.assert_create_token_type_ids(tokenizer_r, tokenizer_p)
|
||||
self.assert_prepare_for_model(tokenizer_r, tokenizer_p)
|
||||
# TODO: enable for v3.0.0
|
||||
# self.assert_empty_output_no_special_tokens(tokenizer_r, tokenizer_p)
|
||||
|
||||
@@ -709,6 +710,12 @@ class CommonFastTokenizerTest(unittest.TestCase):
|
||||
for i_no, i_with in zip(no_special_tokens[key], with_special_tokens[key]):
|
||||
self.assertEqual(len(i_no), len(i_with) - simple_num_special_tokens_to_add)
|
||||
|
||||
def assert_prepare_for_model(self, tokenizer_r, tokenizer_p):
|
||||
string_sequence = "Asserting that both tokenizers are equal"
|
||||
python_output = tokenizer_p.prepare_for_model(tokenizer_p.encode(string_sequence))
|
||||
rust_output = tokenizer_r.prepare_for_model(tokenizer_r.encode(string_sequence))
|
||||
self.assertEqual(python_output, rust_output)
|
||||
|
||||
|
||||
class WordPieceFastTokenizerTest(CommonFastTokenizerTest):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user