@@ -1,4 +1,5 @@ | |||
import functools | |||
class DummyClass: | |||
pass | |||
def __call__(self, *args, **kwargs): | |||
return |
@@ -0,0 +1 @@ | |||
"""基于 transformers-4.11.3 版本迁移""" |
@@ -0,0 +1,9 @@ | |||
""" | |||
为了防止因 https://github.com/huggingface/transformers 版本变化导致代码不兼容,当前 folder 以及子 folder | |||
都复制自 https://github.com/huggingface/transformers 的4.11.3版本。 | |||
In order to avoid the code change of https://github.com/huggingface/transformers to cause version | |||
mismatch, we copy code from https://github.com/huggingface/transformers(version:4.11.3) in this | |||
folder and its subfolder. | |||
""" | |||
__version__ = "4.11.3" | |||
from .models import * |
@@ -0,0 +1,125 @@ | |||
# Copyright 2020 The HuggingFace Team. 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 math | |||
from packaging import version | |||
from fastNLP.envs.imports import _NEED_IMPORT_TORCH | |||
from fastNLP.core.log import logger | |||
if _NEED_IMPORT_TORCH: | |||
import torch | |||
from torch import nn, tanh, sigmoid | |||
from torch.nn.functional import relu | |||
else: | |||
from fastNLP.core.utils.dummy_class import ( | |||
DummyClass as relu, | |||
DummyClass as tanh, | |||
DummyClass as sigmoid, | |||
) | |||
def _gelu_python(x): | |||
""" | |||
Original Implementation of the GELU activation function in Google BERT repo when initially created. For | |||
information: OpenAI GPT's GELU is slightly different (and gives slightly different results): 0.5 * x * (1 + | |||
torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3)))) This is now written in C in nn.functional | |||
Also see the Gaussian Error Linear Units paper: https://arxiv.org/abs/1606.08415 | |||
""" | |||
return x * 0.5 * (1.0 + torch.erf(x / math.sqrt(2.0))) | |||
def gelu_new(x): | |||
""" | |||
Implementation of the GELU activation function currently in Google BERT repo (identical to OpenAI GPT). Also see | |||
the Gaussian Error Linear Units paper: https://arxiv.org/abs/1606.08415 | |||
""" | |||
return 0.5 * x * (1.0 + torch.tanh(math.sqrt(2.0 / math.pi) * (x + 0.044715 * torch.pow(x, 3.0)))) | |||
if _NEED_IMPORT_TORCH: | |||
if version.parse(torch.__version__) < version.parse("1.4"): | |||
gelu = _gelu_python | |||
else: | |||
gelu = nn.functional.gelu | |||
else: | |||
from fastNLP.core.utils.dummy_class import DummyClass as gelu | |||
def gelu_fast(x): | |||
return 0.5 * x * (1.0 + torch.tanh(x * 0.7978845608 * (1.0 + 0.044715 * x * x))) | |||
def quick_gelu(x): | |||
return x * torch.sigmoid(1.702 * x) | |||
def _silu_python(x): | |||
""" | |||
See Gaussian Error Linear Units (Hendrycks et al., https://arxiv.org/abs/1606.08415) where the SiLU (Sigmoid Linear | |||
Unit) was originally introduced and coined, and see Sigmoid-Weighted Linear Units for Neural Network Function | |||
Approximation in Reinforcement Learning (Elfwing et al., https://arxiv.org/abs/1702.03118) and Swish: a Self-Gated | |||
Activation Function (Ramachandran et al., https://arxiv.org/abs/1710.05941v1) where the SiLU was experimented with | |||
later. | |||
""" | |||
return x * torch.sigmoid(x) | |||
if _NEED_IMPORT_TORCH: | |||
if version.parse(torch.__version__) < version.parse("1.7"): | |||
silu = _silu_python | |||
else: | |||
silu = nn.functional.silu | |||
else: | |||
from fastNLP.core.utils.dummy_class import DummyClass as silu | |||
def _mish_python(x): | |||
""" | |||
See Mish: A Self-Regularized Non-Monotonic Activation Function (Misra., https://arxiv.org/abs/1908.08681). Also | |||
visit the official repository for the paper: https://github.com/digantamisra98/Mish | |||
""" | |||
return x * torch.tanh(nn.functional.softplus(x)) | |||
if _NEED_IMPORT_TORCH: | |||
if version.parse(torch.__version__) < version.parse("1.9"): | |||
mish = _mish_python | |||
else: | |||
mish = nn.functional.mish | |||
else: | |||
from fastNLP.core.utils.dummy_class import DummyClass as mish | |||
def linear_act(x): | |||
return x | |||
ACT2FN = { | |||
"relu": relu, | |||
"silu": silu, | |||
"swish": silu, | |||
"gelu": gelu, | |||
"tanh": tanh, | |||
"gelu_new": gelu_new, | |||
"gelu_fast": gelu_fast, | |||
"quick_gelu": quick_gelu, | |||
"mish": mish, | |||
"linear": linear_act, | |||
"sigmoid": sigmoid, | |||
} | |||
def get_activation(activation_string): | |||
if activation_string in ACT2FN: | |||
return ACT2FN[activation_string] | |||
else: | |||
raise KeyError(f"function {activation_string} not found in ACT2FN mapping {list(ACT2FN.keys())}") |
@@ -0,0 +1,777 @@ | |||
# 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. | |||
""" Configuration base class and utilities.""" | |||
import copy | |||
import json | |||
import os | |||
from typing import Any, Dict, Tuple, Union | |||
from . import __version__ | |||
from .file_utils import ( | |||
CONFIG_NAME, | |||
cached_path, | |||
hf_bucket_url, | |||
is_offline_mode, | |||
is_remote_url, | |||
) | |||
from fastNLP.envs.imports import _NEED_IMPORT_TORCH | |||
from fastNLP.core.log import logger | |||
class PretrainedConfig: | |||
r""" | |||
Base class for all configuration classes. Handles a few parameters common to all models' configurations as well as | |||
methods for loading/downloading/saving configurations. | |||
Note: | |||
A configuration file can be loaded and saved to disk. Loading the configuration file and using this file to | |||
initialize a model does **not** load the model weights. It only affects the model's configuration. | |||
Class attributes (overridden by derived classes) | |||
- **model_type** (:obj:`str`) -- An identifier for the model type, serialized into the JSON file, and used to | |||
recreate the correct object in :class:`~transformers.AutoConfig`. | |||
- **is_composition** (:obj:`bool`) -- Whether the config class is composed of multiple sub-configs. In this | |||
case the config has to be initialized from two or more configs of type | |||
:class:`~transformers.PretrainedConfig` like: :class:`~transformers.EncoderDecoderConfig` or | |||
:class:`~RagConfig`. | |||
- **keys_to_ignore_at_inference** (:obj:`List[str]`) -- A list of keys to ignore by default when looking at | |||
dictionary outputs of the model during inference. | |||
- **attribute_map** (:obj:`Dict[str, str]`) -- A dict that maps model specific attribute names to the | |||
standardized naming of attributes. | |||
Common attributes (present in all subclasses) | |||
- **vocab_size** (:obj:`int`) -- The number of tokens in the vocabulary, which is also the first dimension of | |||
the embeddings matrix (this attribute may be missing for models that don't have a text modality like ViT). | |||
- **hidden_size** (:obj:`int`) -- The hidden size of the model. | |||
- **num_attention_heads** (:obj:`int`) -- The number of attention heads used in the multi-head attention layers | |||
of the model. | |||
- **num_hidden_layers** (:obj:`int`) -- The number of blocks in the model. | |||
Args: | |||
name_or_path (:obj:`str`, `optional`, defaults to :obj:`""`): | |||
Store the string that was passed to :func:`~transformers.PreTrainedModel.from_pretrained` or | |||
:func:`~transformers.TFPreTrainedModel.from_pretrained` as ``pretrained_model_name_or_path`` if the | |||
configuration was created with such a method. | |||
output_hidden_states (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not the model should return all hidden-states. | |||
output_attentions (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not the model should returns all attentions. | |||
return_dict (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Whether or not the model should return a :class:`~transformers.file_utils.ModelOutput` instead of a plain | |||
tuple. | |||
is_encoder_decoder (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether the model is used as an encoder/decoder or not. | |||
is_decoder (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether the model is used as decoder or not (in which case it's used as an encoder). | |||
add_cross_attention (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether cross-attention layers should be added to the model. Note, this option is only relevant for models | |||
that can be used as decoder models within the `:class:~transformers.EncoderDecoderModel` class, which | |||
consists of all models in ``AUTO_MODELS_FOR_CAUSAL_LM``. | |||
tie_encoder_decoder (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether all encoder weights should be tied to their equivalent decoder weights. This requires the encoder | |||
and decoder model to have the exact same parameter names. | |||
prune_heads (:obj:`Dict[int, List[int]]`, `optional`, defaults to :obj:`{}`): | |||
Pruned heads of the model. The keys are the selected layer indices and the associated values, the list of | |||
heads to prune in said layer. | |||
For instance ``{1: [0, 2], 2: [2, 3]}`` will prune heads 0 and 2 on layer 1 and heads 2 and 3 on layer 2. | |||
chunk_size_feed_forward (:obj:`int`, `optional`, defaults to :obj:`0`): | |||
The chunk size of all feed forward layers in the residual attention blocks. A chunk size of :obj:`0` means | |||
that the feed forward layer is not chunked. A chunk size of n means that the feed forward layer processes | |||
:obj:`n` < sequence_length embeddings at a time. For more information on feed forward chunking, see `How | |||
does Feed Forward Chunking work? <../glossary.html#feed-forward-chunking>`__ . | |||
Parameters for sequence generation | |||
- **max_length** (:obj:`int`, `optional`, defaults to 20) -- Maximum length that will be used by default in the | |||
:obj:`generate` method of the model. | |||
- **min_length** (:obj:`int`, `optional`, defaults to 10) -- Minimum length that will be used by default in the | |||
:obj:`generate` method of the model. | |||
- **do_sample** (:obj:`bool`, `optional`, defaults to :obj:`False`) -- Flag that will be used by default in the | |||
:obj:`generate` method of the model. Whether or not to use sampling ; use greedy decoding otherwise. | |||
- **early_stopping** (:obj:`bool`, `optional`, defaults to :obj:`False`) -- Flag that will be used by default | |||
in the :obj:`generate` method of the model. Whether to stop the beam search when at least ``num_beams`` | |||
sentences are finished per batch or not. | |||
- **num_beams** (:obj:`int`, `optional`, defaults to 1) -- Number of beams for beam search that will be used by | |||
default in the :obj:`generate` method of the model. 1 means no beam search. | |||
- **num_beam_groups** (:obj:`int`, `optional`, defaults to 1) -- Number of groups to divide :obj:`num_beams` | |||
into in order to ensure diversity among different groups of beams that will be used by default in the | |||
:obj:`generate` method of the model. 1 means no group beam search. | |||
- **diversity_penalty** (:obj:`float`, `optional`, defaults to 0.0) -- Value to control diversity for group | |||
beam search. that will be used by default in the :obj:`generate` method of the model. 0 means no diversity | |||
penalty. The higher the penalty, the more diverse are the outputs. | |||
- **temperature** (:obj:`float`, `optional`, defaults to 1) -- The value used to module the next token | |||
probabilities that will be used by default in the :obj:`generate` method of the model. Must be strictly | |||
positive. | |||
- **top_k** (:obj:`int`, `optional`, defaults to 50) -- Number of highest probability vocabulary tokens to keep | |||
for top-k-filtering that will be used by default in the :obj:`generate` method of the model. | |||
- **top_p** (:obj:`float`, `optional`, defaults to 1) -- Value that will be used by default in the | |||
:obj:`generate` method of the model for ``top_p``. If set to float < 1, only the most probable tokens with | |||
probabilities that add up to ``top_p`` or higher are kept for generation. | |||
- **repetition_penalty** (:obj:`float`, `optional`, defaults to 1) -- Parameter for repetition penalty that | |||
will be used by default in the :obj:`generate` method of the model. 1.0 means no penalty. | |||
- **length_penalty** (:obj:`float`, `optional`, defaults to 1) -- Exponential penalty to the length that will | |||
be used by default in the :obj:`generate` method of the model. | |||
- **no_repeat_ngram_size** (:obj:`int`, `optional`, defaults to 0) -- Value that will be used by default in the | |||
:obj:`generate` method of the model for ``no_repeat_ngram_size``. If set to int > 0, all ngrams of that size | |||
can only occur once. | |||
- **encoder_no_repeat_ngram_size** (:obj:`int`, `optional`, defaults to 0) -- Value that will be used by | |||
default in the :obj:`generate` method of the model for ``encoder_no_repeat_ngram_size``. If set to int > 0, | |||
all ngrams of that size that occur in the ``encoder_input_ids`` cannot occur in the ``decoder_input_ids``. | |||
- **bad_words_ids** (:obj:`List[int]`, `optional`) -- List of token ids that are not allowed to be generated | |||
that will be used by default in the :obj:`generate` method of the model. In order to get the tokens of the | |||
words that should not appear in the generated text, use :obj:`tokenizer.encode(bad_word, | |||
add_prefix_space=True)`. | |||
- **num_return_sequences** (:obj:`int`, `optional`, defaults to 1) -- Number of independently computed returned | |||
sequences for each element in the batch that will be used by default in the :obj:`generate` method of the | |||
model. | |||
- **output_scores** (:obj:`bool`, `optional`, defaults to :obj:`False`) -- Whether the model should return the | |||
logits when used for generation | |||
- **return_dict_in_generate** (:obj:`bool`, `optional`, defaults to :obj:`False`) -- Whether the model should | |||
return a :class:`~transformers.file_utils.ModelOutput` instead of a :obj:`torch.LongTensor` | |||
- **forced_bos_token_id** (:obj:`int`, `optional`) -- The id of the token to force as the first generated token | |||
after the :obj:`decoder_start_token_id`. Useful for multilingual models like :doc:`mBART | |||
<../model_doc/mbart>` where the first generated token needs to be the target language token. | |||
- **forced_eos_token_id** (:obj:`int`, `optional`) -- The id of the token to force as the last generated token | |||
when :obj:`max_length` is reached. | |||
- **remove_invalid_values** (:obj:`bool`, `optional`) -- Whether to remove possible `nan` and `inf` outputs of | |||
the model to prevent the generation method to crash. Note that using ``remove_invalid_values`` can slow down | |||
generation. | |||
Parameters for fine-tuning tasks | |||
- **architectures** (:obj:`List[str]`, `optional`) -- Model architectures that can be used with the model | |||
pretrained weights. | |||
- **finetuning_task** (:obj:`str`, `optional`) -- Name of the task used to fine-tune the model. This can be | |||
used when converting from an original (TensorFlow or PyTorch) checkpoint. | |||
- **id2label** (:obj:`Dict[int, str]`, `optional`) -- A map from index (for instance prediction index, or | |||
target index) to label. | |||
- **label2id** (:obj:`Dict[str, int]`, `optional`) -- A map from label to index for the model. | |||
- **num_labels** (:obj:`int`, `optional`) -- Number of labels to use in the last layer added to the model, | |||
typically for a classification task. | |||
- **task_specific_params** (:obj:`Dict[str, Any]`, `optional`) -- Additional keyword arguments to store for the | |||
current task. | |||
- **problem_type** (:obj:`str`, `optional`) -- Problem type for :obj:`XxxForSequenceClassification` models. Can | |||
be one of (:obj:`"regression"`, :obj:`"single_label_classification"`, :obj:`"multi_label_classification"`). | |||
Please note that this parameter is only available in the following models: `AlbertForSequenceClassification`, | |||
`BertForSequenceClassification`, `BigBirdForSequenceClassification`, `ConvBertForSequenceClassification`, | |||
`DistilBertForSequenceClassification`, `ElectraForSequenceClassification`, `FunnelForSequenceClassification`, | |||
`LongformerForSequenceClassification`, `MobileBertForSequenceClassification`, | |||
`ReformerForSequenceClassification`, `RobertaForSequenceClassification`, | |||
`SqueezeBertForSequenceClassification`, `XLMForSequenceClassification` and `XLNetForSequenceClassification`. | |||
Parameters linked to the tokenizer | |||
- **tokenizer_class** (:obj:`str`, `optional`) -- The name of the associated tokenizer class to use (if none is | |||
set, will use the tokenizer associated to the model by default). | |||
- **prefix** (:obj:`str`, `optional`) -- A specific prompt that should be added at the beginning of each text | |||
before calling the model. | |||
- **bos_token_id** (:obj:`int`, `optional`)) -- The id of the `beginning-of-stream` token. | |||
- **pad_token_id** (:obj:`int`, `optional`)) -- The id of the `padding` token. | |||
- **eos_token_id** (:obj:`int`, `optional`)) -- The id of the `end-of-stream` token. | |||
- **decoder_start_token_id** (:obj:`int`, `optional`)) -- If an encoder-decoder model starts decoding with a | |||
different token than `bos`, the id of that token. | |||
- **sep_token_id** (:obj:`int`, `optional`)) -- The id of the `separation` token. | |||
PyTorch specific parameters | |||
- **torchscript** (:obj:`bool`, `optional`, defaults to :obj:`False`) -- Whether or not the model should be | |||
used with Torchscript. | |||
- **tie_word_embeddings** (:obj:`bool`, `optional`, defaults to :obj:`True`) -- Whether the model's input and | |||
output word embeddings should be tied. Note that this is only relevant if the model has a output word | |||
embedding layer. | |||
- **torch_dtype** (:obj:`str`, `optional`) -- The :obj:`dtype` of the weights. This attribute can be used to | |||
initialize the model to a non-default ``dtype`` (which is normally ``float32``) and thus allow for optimal | |||
storage allocation. For example, if the saved model is ``float16``, ideally we want to load it back using the | |||
minimal amount of memory needed to load ``float16`` weights. Since the config object is stored in plain text, | |||
this attribute contains just the floating type string without the ``torch.`` prefix. For example, for | |||
``torch.float16`` ``torch_dtype`` is the ``"float16"`` string. | |||
This attribute is currently not being used during model loading time, but this may change in the future | |||
versions. But we can already start preparing for the future by saving the dtype with save_pretrained. | |||
TensorFlow specific parameters | |||
- **use_bfloat16** (:obj:`bool`, `optional`, defaults to :obj:`False`) -- Whether or not the model should use | |||
BFloat16 scalars (only used by some TensorFlow models). | |||
""" | |||
model_type: str = "" | |||
is_composition: bool = False | |||
attribute_map: Dict[str, str] = {} | |||
def __setattr__(self, key, value): | |||
if key in super().__getattribute__("attribute_map"): | |||
key = super().__getattribute__("attribute_map")[key] | |||
super().__setattr__(key, value) | |||
def __getattribute__(self, key): | |||
if key != "attribute_map" and key in super().__getattribute__("attribute_map"): | |||
key = super().__getattribute__("attribute_map")[key] | |||
return super().__getattribute__(key) | |||
def __init__(self, **kwargs): | |||
# Attributes with defaults | |||
self.return_dict = kwargs.pop("return_dict", True) | |||
self.output_hidden_states = kwargs.pop("output_hidden_states", False) | |||
self.output_attentions = kwargs.pop("output_attentions", False) | |||
self.torchscript = kwargs.pop("torchscript", False) # Only used by PyTorch models | |||
self.torch_dtype = kwargs.pop("torch_dtype", None) # Only used by PyTorch models | |||
self.use_bfloat16 = kwargs.pop("use_bfloat16", False) | |||
self.pruned_heads = kwargs.pop("pruned_heads", {}) | |||
self.tie_word_embeddings = kwargs.pop( | |||
"tie_word_embeddings", True | |||
) # Whether input and output word embeddings should be tied for all MLM, LM and Seq2Seq models. | |||
# Is decoder is used in encoder-decoder models to differentiate encoder from decoder | |||
self.is_encoder_decoder = kwargs.pop("is_encoder_decoder", False) | |||
self.is_decoder = kwargs.pop("is_decoder", False) | |||
self.add_cross_attention = kwargs.pop("add_cross_attention", False) | |||
self.tie_encoder_decoder = kwargs.pop("tie_encoder_decoder", False) | |||
# Parameters for sequence generation | |||
self.max_length = kwargs.pop("max_length", 20) | |||
self.min_length = kwargs.pop("min_length", 0) | |||
self.do_sample = kwargs.pop("do_sample", False) | |||
self.early_stopping = kwargs.pop("early_stopping", False) | |||
self.num_beams = kwargs.pop("num_beams", 1) | |||
self.num_beam_groups = kwargs.pop("num_beam_groups", 1) | |||
self.diversity_penalty = kwargs.pop("diversity_penalty", 0.0) | |||
self.temperature = kwargs.pop("temperature", 1.0) | |||
self.top_k = kwargs.pop("top_k", 50) | |||
self.top_p = kwargs.pop("top_p", 1.0) | |||
self.repetition_penalty = kwargs.pop("repetition_penalty", 1.0) | |||
self.length_penalty = kwargs.pop("length_penalty", 1.0) | |||
self.no_repeat_ngram_size = kwargs.pop("no_repeat_ngram_size", 0) | |||
self.encoder_no_repeat_ngram_size = kwargs.pop("encoder_no_repeat_ngram_size", 0) | |||
self.bad_words_ids = kwargs.pop("bad_words_ids", None) | |||
self.num_return_sequences = kwargs.pop("num_return_sequences", 1) | |||
self.chunk_size_feed_forward = kwargs.pop("chunk_size_feed_forward", 0) | |||
self.output_scores = kwargs.pop("output_scores", False) | |||
self.return_dict_in_generate = kwargs.pop("return_dict_in_generate", False) | |||
self.forced_bos_token_id = kwargs.pop("forced_bos_token_id", None) | |||
self.forced_eos_token_id = kwargs.pop("forced_eos_token_id", None) | |||
self.remove_invalid_values = kwargs.pop("remove_invalid_values", False) | |||
# Fine-tuning task arguments | |||
self.architectures = kwargs.pop("architectures", None) | |||
self.finetuning_task = kwargs.pop("finetuning_task", None) | |||
self.id2label = kwargs.pop("id2label", None) | |||
self.label2id = kwargs.pop("label2id", None) | |||
if self.id2label is not None: | |||
kwargs.pop("num_labels", None) | |||
self.id2label = dict((int(key), value) for key, value in self.id2label.items()) | |||
# Keys are always strings in JSON so convert ids to int here. | |||
else: | |||
self.num_labels = kwargs.pop("num_labels", 2) | |||
if self.torch_dtype is not None and isinstance(self.torch_dtype, str): | |||
# we will start using self.torch_dtype in v5, but to be consistent with | |||
# from_pretrained's torch_dtype arg convert it to an actual torch.dtype object | |||
if _NEED_IMPORT_TORCH: | |||
import torch | |||
self.torch_dtype = getattr(torch, self.torch_dtype) | |||
# Tokenizer arguments TODO: eventually tokenizer and models should share the same config | |||
self.tokenizer_class = kwargs.pop("tokenizer_class", None) | |||
self.prefix = kwargs.pop("prefix", None) | |||
self.bos_token_id = kwargs.pop("bos_token_id", None) | |||
self.pad_token_id = kwargs.pop("pad_token_id", None) | |||
self.eos_token_id = kwargs.pop("eos_token_id", None) | |||
self.sep_token_id = kwargs.pop("sep_token_id", None) | |||
self.decoder_start_token_id = kwargs.pop("decoder_start_token_id", None) | |||
# task specific arguments | |||
self.task_specific_params = kwargs.pop("task_specific_params", None) | |||
# regression / multi-label classification | |||
self.problem_type = kwargs.pop("problem_type", None) | |||
allowed_problem_types = ("regression", "single_label_classification", "multi_label_classification") | |||
if self.problem_type is not None and self.problem_type not in allowed_problem_types: | |||
raise ValueError( | |||
f"The config parameter `problem_type` was not understood: received {self.problem_type}" | |||
"but only 'regression', 'single_label_classification' and 'multi_label_classification' are valid." | |||
) | |||
# TPU arguments | |||
if kwargs.pop("xla_device", None) is not None: | |||
logger.warning( | |||
"The `xla_device` argument has been deprecated in v4.4.0 of Transformers. It is ignored and you can " | |||
"safely remove it from your `config.json` file." | |||
) | |||
# Name or path to the pretrained checkpoint | |||
self._name_or_path = str(kwargs.pop("name_or_path", "")) | |||
# Drop the transformers version info | |||
self.transformers_version = kwargs.pop("transformers_version", None) | |||
# Deal with gradient checkpointing | |||
if kwargs.get("gradient_checkpointing", False): | |||
logger.warn( | |||
"Passing `gradient_checkpointing` to a config initialization is deprecated and will be removed in v5 " | |||
"Transformers. Using `model.gradient_checkpointing_enable()` instead, or if you are using the " | |||
"`Trainer` API, pass `gradient_checkpointing=True` in your `TrainingArguments`." | |||
) | |||
# Additional attributes without default values | |||
for key, value in kwargs.items(): | |||
try: | |||
setattr(self, key, value) | |||
except AttributeError as err: | |||
logger.error(f"Can't set {key} with value {value} for {self}") | |||
raise err | |||
@property | |||
def name_or_path(self) -> str: | |||
return self._name_or_path | |||
@name_or_path.setter | |||
def name_or_path(self, value): | |||
self._name_or_path = str(value) # Make sure that name_or_path is a string (for JSON encoding) | |||
@property | |||
def use_return_dict(self) -> bool: | |||
""" | |||
:obj:`bool`: Whether or not return :class:`~transformers.file_utils.ModelOutput` instead of tuples. | |||
""" | |||
# If torchscript is set, force `return_dict=False` to avoid jit errors | |||
return self.return_dict and not self.torchscript | |||
@property | |||
def num_labels(self) -> int: | |||
""" | |||
:obj:`int`: The number of labels for classification models. | |||
""" | |||
return len(self.id2label) | |||
@num_labels.setter | |||
def num_labels(self, num_labels: int): | |||
if not hasattr(self, "id2label") or self.id2label is None or len(self.id2label) != num_labels: | |||
self.id2label = {i: f"LABEL_{i}" for i in range(num_labels)} | |||
self.label2id = dict(zip(self.id2label.values(), self.id2label.keys())) | |||
def save_pretrained(self, save_directory: Union[str, os.PathLike], **kwargs): | |||
""" | |||
Save a configuration object to the directory ``save_directory``, so that it can be re-loaded using the | |||
:func:`~transformers.PretrainedConfig.from_pretrained` class method. | |||
Args: | |||
save_directory (:obj:`str` or :obj:`os.PathLike`): | |||
Directory where the configuration JSON file will be saved (will be created if it does not exist). | |||
push_to_hub (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not to push your model to the Hugging Face model hub after saving it. | |||
.. warning:: | |||
Using :obj:`push_to_hub=True` will synchronize the repository you are pushing to with | |||
:obj:`save_directory`, which requires :obj:`save_directory` to be a local clone of the repo you are | |||
pushing to if it's an existing folder. Pass along :obj:`temp_dir=True` to use a temporary directory | |||
instead. | |||
kwargs: | |||
Additional key word arguments passed along to the | |||
:meth:`~transformers.file_utils.PushToHubMixin.push_to_hub` method. | |||
""" | |||
if os.path.isfile(save_directory): | |||
raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file") | |||
os.makedirs(save_directory, exist_ok=True) | |||
# If we save using the predefined names, we can load using `from_pretrained` | |||
output_config_file = os.path.join(save_directory, CONFIG_NAME) | |||
self.to_json_file(output_config_file, use_diff=True) | |||
logger.info(f"Configuration saved in {output_config_file}") | |||
@classmethod | |||
def from_pretrained(cls, pretrained_model_name_or_path: Union[str, os.PathLike], **kwargs) -> "PretrainedConfig": | |||
r""" | |||
Instantiate a :class:`~transformers.PretrainedConfig` (or a derived class) from a pretrained model | |||
configuration. | |||
Args: | |||
pretrained_model_name_or_path (:obj:`str` or :obj:`os.PathLike`): | |||
This can be either: | |||
- a string, the `model id` of a pretrained model configuration hosted inside a model repo on | |||
huggingface.co. Valid model ids can be located at the root-level, like ``bert-base-uncased``, or | |||
namespaced under a user or organization name, like ``dbmdz/bert-base-german-cased``. | |||
- a path to a `directory` containing a configuration file saved using the | |||
:func:`~transformers.PretrainedConfig.save_pretrained` method, e.g., ``./my_model_directory/``. | |||
- a path or url to a saved configuration JSON `file`, e.g., | |||
``./my_model_directory/configuration.json``. | |||
cache_dir (:obj:`str` or :obj:`os.PathLike`, `optional`): | |||
Path to a directory in which a downloaded pretrained model configuration should be cached if the | |||
standard cache should not be used. | |||
force_download (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not to force to (re-)download the configuration files and override the cached versions if | |||
they exist. | |||
resume_download (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not to delete incompletely received file. Attempts to resume the download if such a file | |||
exists. | |||
proxies (:obj:`Dict[str, str]`, `optional`): | |||
A dictionary of proxy servers to use by protocol or endpoint, e.g., :obj:`{'http': 'foo.bar:3128', | |||
'http://hostname': 'foo.bar:4012'}.` The proxies are used on each request. | |||
use_auth_token (:obj:`str` or `bool`, `optional`): | |||
The token to use as HTTP bearer authorization for remote files. If :obj:`True`, will use the token | |||
generated when running :obj:`transformers-cli login` (stored in :obj:`~/.huggingface`). | |||
revision(:obj:`str`, `optional`, defaults to :obj:`"main"`): | |||
The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a | |||
git-based system for storing models and other artifacts on huggingface.co, so ``revision`` can be any | |||
identifier allowed by git. | |||
return_unused_kwargs (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
If :obj:`False`, then this function returns just the final configuration object. | |||
If :obj:`True`, then this functions returns a :obj:`Tuple(config, unused_kwargs)` where `unused_kwargs` | |||
is a dictionary consisting of the key/value pairs whose keys are not configuration attributes: i.e., | |||
the part of ``kwargs`` which has not been used to update ``config`` and is otherwise ignored. | |||
kwargs (:obj:`Dict[str, Any]`, `optional`): | |||
The values in kwargs of any keys which are configuration attributes will be used to override the loaded | |||
values. Behavior concerning key/value pairs whose keys are *not* configuration attributes is controlled | |||
by the ``return_unused_kwargs`` keyword parameter. | |||
.. note:: | |||
Passing :obj:`use_auth_token=True` is required when you want to use a private model. | |||
Returns: | |||
:class:`PretrainedConfig`: The configuration object instantiated from this pretrained model. | |||
Examples:: | |||
# We can't instantiate directly the base class `PretrainedConfig` so let's show the examples on a | |||
# derived class: BertConfig | |||
config = BertConfig.from_pretrained('bert-base-uncased') # Download configuration from huggingface.co and cache. | |||
config = BertConfig.from_pretrained('./test/saved_model/') # E.g. config (or model) was saved using `save_pretrained('./test/saved_model/')` | |||
config = BertConfig.from_pretrained('./test/saved_model/my_configuration.json') | |||
config = BertConfig.from_pretrained('bert-base-uncased', output_attentions=True, foo=False) | |||
assert config.output_attentions == True | |||
config, unused_kwargs = BertConfig.from_pretrained('bert-base-uncased', output_attentions=True, | |||
foo=False, return_unused_kwargs=True) | |||
assert config.output_attentions == True | |||
assert unused_kwargs == {'foo': False} | |||
""" | |||
config_dict, kwargs = cls.get_config_dict(pretrained_model_name_or_path, **kwargs) | |||
if "model_type" in config_dict and hasattr(cls, "model_type") and config_dict["model_type"] != cls.model_type: | |||
logger.warn( | |||
f"You are using a model of type {config_dict['model_type']} to instantiate a model of type " | |||
f"{cls.model_type}. This is not supported for all configurations of models and can yield errors." | |||
) | |||
return cls.from_dict(config_dict, **kwargs) | |||
@classmethod | |||
def get_config_dict( | |||
cls, pretrained_model_name_or_path: Union[str, os.PathLike], **kwargs | |||
) -> Tuple[Dict[str, Any], Dict[str, Any]]: | |||
""" | |||
From a ``pretrained_model_name_or_path``, resolve to a dictionary of parameters, to be used for instantiating a | |||
:class:`~transformers.PretrainedConfig` using ``from_dict``. | |||
Parameters: | |||
pretrained_model_name_or_path (:obj:`str` or :obj:`os.PathLike`): | |||
The identifier of the pre-trained checkpoint from which we want the dictionary of parameters. | |||
Returns: | |||
:obj:`Tuple[Dict, Dict]`: The dictionary(ies) that will be used to instantiate the configuration object. | |||
""" | |||
cache_dir = kwargs.pop("cache_dir", None) | |||
force_download = kwargs.pop("force_download", False) | |||
resume_download = kwargs.pop("resume_download", False) | |||
proxies = kwargs.pop("proxies", None) | |||
use_auth_token = kwargs.pop("use_auth_token", None) | |||
local_files_only = kwargs.pop("local_files_only", False) | |||
revision = kwargs.pop("revision", None) | |||
from_pipeline = kwargs.pop("_from_pipeline", None) | |||
from_auto_class = kwargs.pop("_from_auto", False) | |||
user_agent = {"file_type": "config", "from_auto_class": from_auto_class} | |||
if from_pipeline is not None: | |||
user_agent["using_pipeline"] = from_pipeline | |||
if is_offline_mode() and not local_files_only: | |||
logger.info("Offline mode: forcing local_files_only=True") | |||
local_files_only = True | |||
pretrained_model_name_or_path = str(pretrained_model_name_or_path) | |||
if os.path.isdir(pretrained_model_name_or_path): | |||
config_file = os.path.join(pretrained_model_name_or_path, CONFIG_NAME) | |||
elif os.path.isfile(pretrained_model_name_or_path) or is_remote_url(pretrained_model_name_or_path): | |||
config_file = pretrained_model_name_or_path | |||
else: | |||
config_file = hf_bucket_url( | |||
pretrained_model_name_or_path, filename=CONFIG_NAME, revision=revision, mirror=None | |||
) | |||
try: | |||
# Load from URL or cache if already cached | |||
resolved_config_file = cached_path( | |||
config_file, | |||
cache_dir=cache_dir, | |||
force_download=force_download, | |||
proxies=proxies, | |||
resume_download=resume_download, | |||
local_files_only=local_files_only, | |||
use_auth_token=use_auth_token, | |||
user_agent=user_agent, | |||
) | |||
# Load config dict | |||
config_dict = cls._dict_from_json_file(resolved_config_file) | |||
except EnvironmentError as err: | |||
logger.error(err) | |||
msg = ( | |||
f"Can't load config for '{pretrained_model_name_or_path}'. Make sure that:\n\n" | |||
f"- '{pretrained_model_name_or_path}' is a correct model identifier listed on 'https://huggingface.co/models'\n\n" | |||
f"- or '{pretrained_model_name_or_path}' is the correct path to a directory containing a {CONFIG_NAME} file\n\n" | |||
) | |||
if revision is not None: | |||
msg += f"- or '{revision}' is a valid git identifier (branch name, a tag name, or a commit id) that exists for this model name as listed on its model page on 'https://huggingface.co/models'\n\n" | |||
raise EnvironmentError(msg) | |||
except (json.JSONDecodeError, UnicodeDecodeError): | |||
msg = ( | |||
f"Couldn't reach server at '{config_file}' to download configuration file or " | |||
"configuration file is not a valid JSON file. " | |||
f"Please check network or file content here: {resolved_config_file}." | |||
) | |||
raise EnvironmentError(msg) | |||
if resolved_config_file == config_file: | |||
logger.info(f"loading configuration file {config_file}") | |||
else: | |||
logger.info(f"loading configuration file {config_file} from cache at {resolved_config_file}") | |||
return config_dict, kwargs | |||
@classmethod | |||
def from_dict(cls, config_dict: Dict[str, Any], **kwargs) -> "PretrainedConfig": | |||
""" | |||
Instantiates a :class:`~transformers.PretrainedConfig` from a Python dictionary of parameters. | |||
Args: | |||
config_dict (:obj:`Dict[str, Any]`): | |||
Dictionary that will be used to instantiate the configuration object. Such a dictionary can be | |||
retrieved from a pretrained checkpoint by leveraging the | |||
:func:`~transformers.PretrainedConfig.get_config_dict` method. | |||
kwargs (:obj:`Dict[str, Any]`): | |||
Additional parameters from which to initialize the configuration object. | |||
Returns: | |||
:class:`PretrainedConfig`: The configuration object instantiated from those parameters. | |||
""" | |||
return_unused_kwargs = kwargs.pop("return_unused_kwargs", False) | |||
config = cls(**config_dict) | |||
if hasattr(config, "pruned_heads"): | |||
config.pruned_heads = dict((int(key), value) for key, value in config.pruned_heads.items()) | |||
# Update config with kwargs if needed | |||
to_remove = [] | |||
for key, value in kwargs.items(): | |||
if hasattr(config, key): | |||
setattr(config, key, value) | |||
if key != "torch_dtype": | |||
to_remove.append(key) | |||
for key in to_remove: | |||
kwargs.pop(key, None) | |||
logger.info(f"Model config {config}") | |||
if return_unused_kwargs: | |||
return config, kwargs | |||
else: | |||
return config | |||
@classmethod | |||
def from_json_file(cls, json_file: Union[str, os.PathLike]) -> "PretrainedConfig": | |||
""" | |||
Instantiates a :class:`~transformers.PretrainedConfig` from the path to a JSON file of parameters. | |||
Args: | |||
json_file (:obj:`str` or :obj:`os.PathLike`): | |||
Path to the JSON file containing the parameters. | |||
Returns: | |||
:class:`PretrainedConfig`: The configuration object instantiated from that JSON file. | |||
""" | |||
config_dict = cls._dict_from_json_file(json_file) | |||
return cls(**config_dict) | |||
@classmethod | |||
def _dict_from_json_file(cls, json_file: Union[str, os.PathLike]): | |||
with open(json_file, "r", encoding="utf-8") as reader: | |||
text = reader.read() | |||
return json.loads(text) | |||
def __eq__(self, other): | |||
return self.__dict__ == other.__dict__ | |||
def __repr__(self): | |||
return f"{self.__class__.__name__} {self.to_json_string()}" | |||
def to_diff_dict(self) -> Dict[str, Any]: | |||
""" | |||
Removes all attributes from config which correspond to the default config attributes for better readability and | |||
serializes to a Python dictionary. | |||
Returns: | |||
:obj:`Dict[str, Any]`: Dictionary of all the attributes that make up this configuration instance, | |||
""" | |||
config_dict = self.to_dict() | |||
# get the default config dict | |||
default_config_dict = PretrainedConfig().to_dict() | |||
# get class specific config dict | |||
class_config_dict = self.__class__().to_dict() if not self.is_composition else {} | |||
serializable_config_dict = {} | |||
# only serialize values that differ from the default config | |||
for key, value in config_dict.items(): | |||
if ( | |||
key not in default_config_dict | |||
or key == "transformers_version" | |||
or value != default_config_dict[key] | |||
or (key in class_config_dict and value != class_config_dict[key]) | |||
): | |||
serializable_config_dict[key] = value | |||
self.dict_torch_dtype_to_str(serializable_config_dict) | |||
return serializable_config_dict | |||
def to_dict(self) -> Dict[str, Any]: | |||
""" | |||
Serializes this instance to a Python dictionary. | |||
Returns: | |||
:obj:`Dict[str, Any]`: Dictionary of all the attributes that make up this configuration instance. | |||
""" | |||
output = copy.deepcopy(self.__dict__) | |||
if hasattr(self.__class__, "model_type"): | |||
output["model_type"] = self.__class__.model_type | |||
# Transformers version when serializing the model | |||
output["transformers_version"] = __version__ | |||
self.dict_torch_dtype_to_str(output) | |||
return output | |||
def to_json_string(self, use_diff: bool = True) -> str: | |||
""" | |||
Serializes this instance to a JSON string. | |||
Args: | |||
use_diff (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
If set to ``True``, only the difference between the config instance and the default | |||
``PretrainedConfig()`` is serialized to JSON string. | |||
Returns: | |||
:obj:`str`: String containing all the attributes that make up this configuration instance in JSON format. | |||
""" | |||
if use_diff is True: | |||
config_dict = self.to_diff_dict() | |||
else: | |||
config_dict = self.to_dict() | |||
return json.dumps(config_dict, indent=2, sort_keys=True) + "\n" | |||
def to_json_file(self, json_file_path: Union[str, os.PathLike], use_diff: bool = True): | |||
""" | |||
Save this instance to a JSON file. | |||
Args: | |||
json_file_path (:obj:`str` or :obj:`os.PathLike`): | |||
Path to the JSON file in which this configuration instance's parameters will be saved. | |||
use_diff (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
If set to ``True``, only the difference between the config instance and the default | |||
``PretrainedConfig()`` is serialized to JSON file. | |||
""" | |||
with open(json_file_path, "w", encoding="utf-8") as writer: | |||
writer.write(self.to_json_string(use_diff=use_diff)) | |||
def update(self, config_dict: Dict[str, Any]): | |||
""" | |||
Updates attributes of this class with attributes from ``config_dict``. | |||
Args: | |||
config_dict (:obj:`Dict[str, Any]`): Dictionary of attributes that should be updated for this class. | |||
""" | |||
for key, value in config_dict.items(): | |||
setattr(self, key, value) | |||
def update_from_string(self, update_str: str): | |||
""" | |||
Updates attributes of this class with attributes from ``update_str``. | |||
The expected format is ints, floats and strings as is, and for booleans use ``true`` or ``false``. For example: | |||
"n_embd=10,resid_pdrop=0.2,scale_attn_weights=false,summary_type=cls_index" | |||
The keys to change have to already exist in the config object. | |||
Args: | |||
update_str (:obj:`str`): String with attributes that should be updated for this class. | |||
""" | |||
d = dict(x.split("=") for x in update_str.split(",")) | |||
for k, v in d.items(): | |||
if not hasattr(self, k): | |||
raise ValueError(f"key {k} isn't in the original config dict") | |||
old_v = getattr(self, k) | |||
if isinstance(old_v, bool): | |||
if v.lower() in ["true", "1", "y", "yes"]: | |||
v = True | |||
elif v.lower() in ["false", "0", "n", "no"]: | |||
v = False | |||
else: | |||
raise ValueError(f"can't derive true or false from {v} (key {k})") | |||
elif isinstance(old_v, int): | |||
v = int(v) | |||
elif isinstance(old_v, float): | |||
v = float(v) | |||
elif not isinstance(old_v, str): | |||
raise ValueError( | |||
f"You can only update int, float, bool or string values in the config, got {v} for key {k}" | |||
) | |||
setattr(self, k, v) | |||
def dict_torch_dtype_to_str(self, d: Dict[str, Any]) -> None: | |||
""" | |||
Checks whether the passed dictionary has a `torch_dtype` key and if it's not None, converts torch.dtype to a | |||
string of just the type. For example, :obj:`torch.float32` get converted into `"float32"` string, which can | |||
then be stored in the json format. | |||
""" | |||
if d.get("torch_dtype", None) is not None and not isinstance(d["torch_dtype"], str): | |||
d["torch_dtype"] = str(d["torch_dtype"]).split(".")[1] |
@@ -0,0 +1,388 @@ | |||
# Copyright 2020 The HuggingFace Team. 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. | |||
""" | |||
Integration with Deepspeed | |||
""" | |||
import importlib.util | |||
import io | |||
import json | |||
import weakref | |||
from copy import deepcopy | |||
from functools import partialmethod | |||
from .utils.versions import require_version | |||
from fastNLP.envs.imports import _NEED_IMPORT_TORCH | |||
from fastNLP.core.log import logger | |||
if _NEED_IMPORT_TORCH: | |||
import torch | |||
def is_deepspeed_available(): | |||
return importlib.util.find_spec("deepspeed") is not None | |||
class HfDeepSpeedConfig: | |||
""" | |||
This object contains a DeepSpeed configuration dictionary and can be quickly queried for things like zero stage. | |||
A ``weakref`` of this object is stored in the module's globals to be able to access the config from areas where | |||
things like the Trainer object is not available (e.g. ``from_pretrained`` and ``_get_resized_embeddings``). | |||
Therefore it's important that this object remains alive while the program is still running. | |||
:class:`~transformers.Trainer` uses the ``HfTrainerDeepSpeedConfig`` subclass instead. That subclass has logic to | |||
sync the configuration with values of :class:`~transformers.TrainingArguments` by replacing special placeholder | |||
values: ``"auto"``. Without this special logic the DeepSpeed configuration is not modified in any way. | |||
Args: | |||
config_file_or_dict (:obj:`Union[str, Dict]`): path to DeepSpeed config file or dict. | |||
""" | |||
def __init__(self, config_file_or_dict): | |||
# set global weakref object | |||
set_hf_deepspeed_config(self) | |||
require_version("deepspeed>=0.5.3") | |||
if isinstance(config_file_or_dict, dict): | |||
# Don't modify user's data should they want to reuse it (e.g. in tests), because once we | |||
# modified it, it will not be accepted here again, since `auto` values would have been overridden | |||
config = deepcopy(config_file_or_dict) | |||
elif isinstance(config_file_or_dict, str): | |||
with io.open(config_file_or_dict, "r", encoding="utf-8") as f: | |||
config = json.load(f) | |||
else: | |||
raise ValueError("expecting either a path to a DeepSpeed config file or a pre-populated dict") | |||
self.config = config | |||
# zero stage - this is done as early as possible, before model is created, to allow | |||
# ``is_deepspeed_zero3_enabled`` query and getting to the early deepspeed config object | |||
# during ``zero.Init()`` which needs whether fp16 is enabled, dtype, etc. | |||
self._stage = self.get_value("zero_optimization.stage", -1) | |||
# offload | |||
self._offload = False | |||
if self.is_zero2() or self.is_zero3(): | |||
offload_devices_valid = set(["cpu", "nvme"]) | |||
offload_devices = set( | |||
[ | |||
self.get_value("zero_optimization.offload_optimizer.device"), | |||
self.get_value("zero_optimization.offload_param.device"), | |||
] | |||
) | |||
if len(offload_devices & offload_devices_valid) > 0: | |||
self._offload = True | |||
def find_config_node(self, ds_key_long): | |||
config = self.config | |||
# find the config node of interest if it exists | |||
nodes = ds_key_long.split(".") | |||
ds_key = nodes.pop() | |||
for node in nodes: | |||
config = config.get(node) | |||
if config is None: | |||
return None, ds_key | |||
return config, ds_key | |||
def get_value(self, ds_key_long, default=None): | |||
""" | |||
Returns the set value or ``default`` if no value is set | |||
""" | |||
config, ds_key = self.find_config_node(ds_key_long) | |||
if config is None: | |||
return default | |||
return config.get(ds_key, default) | |||
def is_true(self, ds_key_long): | |||
""" | |||
Returns :obj:`True`/:obj:`False` only if the value is set, always :obj:`False` otherwise. So use this method to | |||
ask the very specific question of whether the value is set to :obj:`True` (and it's not set to :obj:`False` or | |||
isn't set). | |||
""" | |||
value = self.get_value(ds_key_long) | |||
return False if value is None else bool(value) | |||
def is_false(self, ds_key_long): | |||
""" | |||
Returns :obj:`True`/:obj:`False` only if the value is set, always :obj:`False` otherwise. So use this method to | |||
ask the very specific question of whether the value is set to :obj:`False` (and it's not set to :obj:`True` or | |||
isn't set). | |||
""" | |||
value = self.get_value(ds_key_long) | |||
return False if value is None else not bool(value) | |||
def is_zero2(self): | |||
return self._stage == 2 | |||
def is_zero3(self): | |||
return self._stage == 3 | |||
def is_offload(self): | |||
return self._offload | |||
class HfTrainerDeepSpeedConfig(HfDeepSpeedConfig): | |||
""" | |||
The ``HfTrainerDeepSpeedConfig`` object is meant to be created during ``TrainingArguments`` object creation and has | |||
the same lifespan as the latter. | |||
""" | |||
def __init__(self, config_file_or_dict): | |||
super().__init__(config_file_or_dict) | |||
self._dtype = torch.float16 | |||
self.mismatches = [] | |||
def dtype(self): | |||
return self._dtype | |||
def fill_match(self, ds_key_long, hf_val, hf_key=None, must_match=True): | |||
""" | |||
A utility method that massages the config file and can optionally verify that the values match. | |||
1. Replace "auto" values with ``TrainingArguments`` value. | |||
2. If it wasn't "auto" and ``must_match`` is true, then check that DS config matches Trainer | |||
config values and if mismatched add the entry to ``self.mismatched`` - will assert during | |||
``trainer_config_finalize`` for one or more mismatches. | |||
""" | |||
config, ds_key = self.find_config_node(ds_key_long) | |||
if config is None: | |||
return | |||
if config.get(ds_key) == "auto": | |||
config[ds_key] = hf_val | |||
return | |||
if not must_match: | |||
return | |||
ds_val = config.get(ds_key) | |||
if ds_val is not None and ds_val != hf_val: | |||
self.mismatches.append(f"- ds {ds_key_long}={ds_val} vs hf {hf_key}={hf_val}") | |||
fill_only = partialmethod(fill_match, must_match=False) | |||
def trainer_config_process(self, args): | |||
""" | |||
Adjust the config with ``TrainingArguments`` values. This stage is run during ``TrainingArguments`` object | |||
creation. | |||
""" | |||
# DeepSpeed does: | |||
# train_batch_size = world_size * train_micro_batch_size_per_gpu * gradient_accumulation_steps | |||
train_batch_size = args.world_size * args.per_device_train_batch_size * args.gradient_accumulation_steps | |||
self.fill_match( | |||
"train_micro_batch_size_per_gpu", args.per_device_train_batch_size, "per_device_train_batch_size" | |||
) | |||
self.fill_match("gradient_accumulation_steps", args.gradient_accumulation_steps, "gradient_accumulation_steps") | |||
self.fill_match("train_batch_size", train_batch_size, "train_batch_size (calculated)") | |||
self.fill_match("gradient_clipping", args.max_grad_norm, "max_grad_norm") | |||
self.fill_match("optimizer.params.lr", args.learning_rate, "learning_rate") | |||
self.fill_match("optimizer.params.betas", [args.adam_beta1, args.adam_beta2], "adam_beta1+adam_beta2") | |||
self.fill_match("optimizer.params.eps", args.adam_epsilon, "adam_epsilon") | |||
self.fill_match("optimizer.params.weight_decay", args.weight_decay, "weight_decay") | |||
self.fill_only("scheduler.params.warmup_min_lr", 0) # not a trainer arg | |||
self.fill_match("scheduler.params.warmup_max_lr", args.learning_rate, "learning_rate") | |||
# total_num_steps - will get set in trainer_config_finalize | |||
# fp16 | |||
if args.fp16: | |||
fp16_backend = "apex" if args.fp16_backend == "apex" else "amp" | |||
else: | |||
fp16_backend = None | |||
# amp: similar to the pytorch native amp - it has a bunch of optional params but we won't set | |||
# any here unless the user did the work | |||
self.fill_match("fp16.enabled", fp16_backend == "amp", "fp16+fp16_backend(amp)") | |||
# apex: delegates amp work to apex (which needs to be available), but it cannot be used with any | |||
# ZeRO features | |||
self.fill_match("amp.enabled", fp16_backend == "apex", "fp16+fp16_backend(apex)") | |||
self.fill_match("amp.opt_level", args.fp16_opt_level, "fp16_opt_level") | |||
# only if we have an explicit fp16.enabled = False then it's fp32, if it's True or this | |||
# whole config section is missing then the fallback is fp16 | |||
if self.is_false("fp16.enabled"): | |||
self._dtype = torch.float32 | |||
# later there will be other dtypes besides just fp16 and fp32 | |||
# also not quite sure what dtype should be under apex, defaulting to fp16 for now | |||
def trainer_config_finalize(self, args, model, num_training_steps): | |||
""" | |||
This stage is run after we have the model and know num_training_steps. | |||
Now we we can complete the configuration process. | |||
""" | |||
# zero | |||
if self.is_zero3(): | |||
# automatically assign the optimal config values based on model config | |||
hidden_size = model.config.hidden_size | |||
self.fill_only("zero_optimization.reduce_bucket_size", hidden_size * hidden_size) | |||
self.fill_only("zero_optimization.stage3_prefetch_bucket_size", 0.9 * hidden_size * hidden_size) | |||
self.fill_only("zero_optimization.stage3_param_persistence_threshold", 10 * hidden_size) | |||
# scheduler | |||
self.fill_match("scheduler.params.total_num_steps", num_training_steps, "num_training_steps (calculated)") | |||
self.fill_match("scheduler.params.warmup_num_steps", args.get_warmup_steps(num_training_steps), "warmup_steps") | |||
if len(self.mismatches) > 0: | |||
mismatches = "\n".join(self.mismatches) | |||
raise ValueError( | |||
f"Please correct the following DeepSpeed config values that mismatch TrainingArguments values:\n{mismatches}\n" | |||
"The easiest method is to set these DeepSpeed config values to 'auto'." | |||
) | |||
# keep the config object global to be able to access it anywhere during TrainingArguments life-cycle | |||
_hf_deepspeed_config_weak_ref = None | |||
def set_hf_deepspeed_config(hf_deepspeed_config_obj): | |||
# this is a special weakref global object to allow us to get to Deepspeed config from APIs | |||
# that don't have an easy way to get to the Deepspeed config outside of the Trainer domain. | |||
global _hf_deepspeed_config_weak_ref | |||
# will go away automatically when HfDeepSpeedConfig is destroyed (when TrainingArguments is destroyed) | |||
_hf_deepspeed_config_weak_ref = weakref.ref(hf_deepspeed_config_obj) | |||
def is_deepspeed_zero3_enabled(): | |||
if _hf_deepspeed_config_weak_ref is not None and _hf_deepspeed_config_weak_ref() is not None: | |||
return _hf_deepspeed_config_weak_ref().is_zero3() | |||
else: | |||
return False | |||
def deepspeed_config(): | |||
if _hf_deepspeed_config_weak_ref is not None and _hf_deepspeed_config_weak_ref() is not None: | |||
return _hf_deepspeed_config_weak_ref().config | |||
else: | |||
return None | |||
def deepspeed_init(trainer, num_training_steps, resume_from_checkpoint=None): | |||
""" | |||
Init DeepSpeed, after updating the DeepSpeed configuration with any relevant Trainer's args. | |||
If ``resume_from_checkpoint`` was passed then an attempt to resume from a previously saved checkpoint will be made. | |||
Args: | |||
trainer: Trainer object | |||
num_training_steps: per single gpu | |||
resume_from_checkpoint: path to a checkpoint if to resume from after normal DeepSpeedEngine load | |||
Returns: model, optimizer, lr_scheduler | |||
""" | |||
import deepspeed | |||
from deepspeed.utils import logger as ds_logger | |||
model = trainer.model | |||
args = trainer.args | |||
hf_deepspeed_config = args.hf_deepspeed_config | |||
hf_deepspeed_config.trainer_config_finalize(args, model, num_training_steps) | |||
# resume config update - some bits like `model` and `num_training_steps` only become available during train | |||
config = hf_deepspeed_config.config | |||
# Optimizer + Scheduler | |||
# Currently supported combos: | |||
# 1. DS scheduler + DS optimizer: Yes | |||
# 2. HF scheduler + HF optimizer: Yes | |||
# 3. DS scheduler + HF optimizer: Yes | |||
# 4. HF scheduler + DS optimizer: Yes | |||
# | |||
# Unless Offload is enabled in which case it's: | |||
# 1. DS scheduler + DS optimizer: Yes | |||
# 2. HF scheduler + HF optimizer: Mostly* | |||
# 3. DS scheduler + HF optimizer: Mostly* | |||
# 4. HF scheduler + DS optimizer: Yes | |||
# | |||
# Mostly*: All non-native DeepSpeed optimizers that have both CPU and GPU implementation should work (except LAMB) | |||
optimizer = None | |||
if "optimizer" in config: | |||
if args.adafactor: | |||
raise ValueError( | |||
"--adafactor was passed, but also found `optimizer` configured in the DeepSpeed config. " | |||
"Only one optimizer can be configured." | |||
) | |||
else: | |||
if hf_deepspeed_config.is_offload(): | |||
logger.info( | |||
"Detected ZeRO Offload and non-DeepSpeed optimizers: This combination should work as long as the custom optimizer has both CPU and GPU implementation (except LAMB)" | |||
) | |||
# ds supports Adam, OneBitAdam, and Lamb optimizers and can import other optimizers from torch. | |||
# But trainer uses AdamW by default. | |||
optimizer = trainer.create_optimizer() | |||
# To use other optimizers requires voiding warranty with: `zero_allow_untested_optimizer` | |||
config["zero_allow_untested_optimizer"] = True | |||
def _lr_scheduler_callable(optimizer): | |||
return trainer.create_scheduler(num_training_steps=num_training_steps, optimizer=optimizer) | |||
lr_scheduler = None | |||
if "scheduler" not in config: | |||
if optimizer is None: | |||
# Optimizer is not available, so use callable to defer lr_scheduler creation to DS init | |||
lr_scheduler = _lr_scheduler_callable | |||
else: | |||
lr_scheduler = trainer.create_scheduler(num_training_steps=num_training_steps, optimizer=optimizer) | |||
# keep for quick debug: | |||
# from pprint import pprint; pprint(config) | |||
# set the Deepspeed log level consistent with the trainer | |||
ds_logger.setLevel(args.get_process_log_level()) | |||
model_parameters = filter(lambda p: p.requires_grad, model.parameters()) | |||
model, optimizer, _, lr_scheduler = deepspeed.initialize( | |||
model=model, | |||
model_parameters=model_parameters, | |||
config_params=config, | |||
optimizer=optimizer, | |||
lr_scheduler=lr_scheduler, | |||
) | |||
if resume_from_checkpoint is not None: | |||
# it's possible that the user is trying to resume from model_path, which doesn't necessarily | |||
# contain a deepspeed checkpoint. e.g. examples just check if the dir exists and assume it's | |||
# a resume from a checkpoint and not just a local pretrained weight. So we check here if the | |||
# path contains what looks like a deepspeed checkpoint | |||
import glob | |||
deepspeed_checkpoint_dirs = sorted(glob.glob(f"{resume_from_checkpoint}/global_step*")) | |||
if len(deepspeed_checkpoint_dirs) > 0: | |||
logger.info(f"Attempting to resume from {resume_from_checkpoint}") | |||
# this magically updates self.optimizer and self.lr_scheduler | |||
load_path, _ = model.load_checkpoint( | |||
resume_from_checkpoint, load_optimizer_states=True, load_lr_scheduler_states=True | |||
) | |||
if load_path is None: | |||
raise ValueError(f"[deepspeed] failed to resume from checkpoint {resume_from_checkpoint}") | |||
else: | |||
logger.info(f"{resume_from_checkpoint} doesn't have deepspeed checkpoints, doing nothing") | |||
return model, optimizer, lr_scheduler |
@@ -0,0 +1,934 @@ | |||
import copy | |||
import fnmatch | |||
import importlib.util | |||
import io | |||
import json | |||
import os | |||
import re | |||
import shutil | |||
import sys | |||
import tarfile | |||
import tempfile | |||
import operator | |||
from collections import OrderedDict, UserDict | |||
from contextlib import contextmanager | |||
from dataclasses import fields | |||
from enum import Enum | |||
from functools import partial | |||
from hashlib import sha256 | |||
from pathlib import Path | |||
from typing import Any, BinaryIO, Dict, Optional, Tuple, Union | |||
from urllib.parse import urlparse | |||
from uuid import uuid4 | |||
from zipfile import ZipFile, is_zipfile | |||
import numpy as np | |||
# from tqdm.auto import tqdm | |||
import requests | |||
from . import __version__ | |||
from .utils.versions import importlib_metadata | |||
from fastNLP.envs.imports import _NEED_IMPORT_TORCH, _TORCH_GREATER_EQUAL_1_8 | |||
from fastNLP.envs.utils import _compare_version | |||
from fastNLP.core.log import logger | |||
if _NEED_IMPORT_TORCH: | |||
import torch | |||
_torch_version = importlib_metadata.version("torch") | |||
hf_cache_home = os.path.expanduser( | |||
os.getenv("HF_HOME", os.path.join(os.getenv("XDG_CACHE_HOME", "~/.cache"), "huggingface")) | |||
) | |||
default_cache_path = os.path.join(hf_cache_home, "transformers") | |||
PYTORCH_PRETRAINED_BERT_CACHE = os.getenv("PYTORCH_PRETRAINED_BERT_CACHE", default_cache_path) | |||
PYTORCH_TRANSFORMERS_CACHE = os.getenv("PYTORCH_TRANSFORMERS_CACHE", PYTORCH_PRETRAINED_BERT_CACHE) | |||
TRANSFORMERS_CACHE = os.getenv("TRANSFORMERS_CACHE", PYTORCH_TRANSFORMERS_CACHE) | |||
SESSION_ID = uuid4().hex | |||
ENV_VARS_TRUE_VALUES = {"1", "ON", "YES", "TRUE"} | |||
DISABLE_TELEMETRY = os.getenv("DISABLE_TELEMETRY", False) in ENV_VARS_TRUE_VALUES | |||
WEIGHTS_NAME = "pytorch_model.bin" | |||
DUMMY_INPUTS = [[7, 6, 0, 0, 1], [1, 2, 3, 0, 0], [0, 0, 0, 4, 5]] | |||
_staging_mode = os.environ.get("HUGGINGFACE_CO_STAGING", "NO").upper() in ENV_VARS_TRUE_VALUES | |||
_default_endpoint = "https://moon-staging.huggingface.co" if _staging_mode else "https://huggingface.co" | |||
HUGGINGFACE_CO_RESOLVE_ENDPOINT = os.environ.get("HUGGINGFACE_CO_RESOLVE_ENDPOINT", _default_endpoint) | |||
HUGGINGFACE_CO_PREFIX = HUGGINGFACE_CO_RESOLVE_ENDPOINT + "/{model_id}/resolve/{revision}/{filename}" | |||
CONFIG_NAME = "config.json" | |||
_is_offline_mode = True if os.environ.get("TRANSFORMERS_OFFLINE", "0").upper() in ENV_VARS_TRUE_VALUES else False | |||
@contextmanager | |||
def filelock(path): | |||
try: | |||
import fcntl | |||
open_mode = os.O_RDWR | os.O_CREAT | os.O_TRUNC | |||
fd = os.open(path, open_mode) | |||
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) | |||
except: | |||
pass | |||
yield | |||
try: | |||
fcntl.flock(fd, fcntl.LOCK_UN) | |||
os.close(fd) | |||
except: | |||
pass | |||
def is_offline_mode(): | |||
return _is_offline_mode | |||
def is_training_run_on_sagemaker(): | |||
return "SAGEMAKER_JOB_NAME" in os.environ | |||
def add_start_docstrings(*docstr): | |||
def docstring_decorator(fn): | |||
fn.__doc__ = "".join(docstr) + (fn.__doc__ if fn.__doc__ is not None else "") | |||
return fn | |||
return docstring_decorator | |||
def add_start_docstrings_to_model_forward(*docstr): | |||
def docstring_decorator(fn): | |||
class_name = f":class:`~transformers.{fn.__qualname__.split('.')[0]}`" | |||
intro = f" The {class_name} forward method, overrides the :func:`__call__` special method." | |||
note = r""" | |||
.. note:: | |||
Although the recipe for forward pass needs to be defined within this function, one should call the | |||
:class:`Module` instance afterwards instead of this since the former takes care of running the pre and post | |||
processing steps while the latter silently ignores them. | |||
""" | |||
fn.__doc__ = intro + note + "".join(docstr) + (fn.__doc__ if fn.__doc__ is not None else "") | |||
return fn | |||
return docstring_decorator | |||
def add_end_docstrings(*docstr): | |||
def docstring_decorator(fn): | |||
fn.__doc__ = fn.__doc__ + "".join(docstr) | |||
return fn | |||
return docstring_decorator | |||
PT_RETURN_INTRODUCTION = r""" | |||
Returns: | |||
:class:`~{full_output_type}` or :obj:`tuple(torch.FloatTensor)`: A :class:`~{full_output_type}` or a tuple of | |||
:obj:`torch.FloatTensor` (if ``return_dict=False`` is passed or when ``config.return_dict=False``) comprising | |||
various elements depending on the configuration (:class:`~transformers.{config_class}`) and inputs. | |||
""" | |||
def _get_indent(t): | |||
"""Returns the indentation in the first line of t""" | |||
search = re.search(r"^(\s*)\S", t) | |||
return "" if search is None else search.groups()[0] | |||
def _convert_output_args_doc(output_args_doc): | |||
"""Convert output_args_doc to display properly.""" | |||
# Split output_arg_doc in blocks argument/description | |||
indent = _get_indent(output_args_doc) | |||
blocks = [] | |||
current_block = "" | |||
for line in output_args_doc.split("\n"): | |||
# If the indent is the same as the beginning, the line is the name of new arg. | |||
if _get_indent(line) == indent: | |||
if len(current_block) > 0: | |||
blocks.append(current_block[:-1]) | |||
current_block = f"{line}\n" | |||
else: | |||
# Otherwise it's part of the description of the current arg. | |||
# We need to remove 2 spaces to the indentation. | |||
current_block += f"{line[2:]}\n" | |||
blocks.append(current_block[:-1]) | |||
# Format each block for proper rendering | |||
for i in range(len(blocks)): | |||
blocks[i] = re.sub(r"^(\s+)(\S+)(\s+)", r"\1- **\2**\3", blocks[i]) | |||
blocks[i] = re.sub(r":\s*\n\s*(\S)", r" -- \1", blocks[i]) | |||
return "\n".join(blocks) | |||
def _prepare_output_docstrings(output_type, config_class): | |||
""" | |||
Prepares the return part of the docstring using `output_type`. | |||
""" | |||
docstrings = output_type.__doc__ | |||
# Remove the head of the docstring to keep the list of args only | |||
lines = docstrings.split("\n") | |||
i = 0 | |||
while i < len(lines) and re.search(r"^\s*(Args|Parameters):\s*$", lines[i]) is None: | |||
i += 1 | |||
if i < len(lines): | |||
docstrings = "\n".join(lines[(i + 1) :]) | |||
docstrings = _convert_output_args_doc(docstrings) | |||
# Add the return introduction | |||
full_output_type = f"{output_type.__module__}.{output_type.__name__}" | |||
intro = PT_RETURN_INTRODUCTION | |||
intro = intro.format(full_output_type=full_output_type, config_class=config_class) | |||
return intro + docstrings | |||
PT_TOKEN_CLASSIFICATION_SAMPLE = r""" | |||
Example:: | |||
>>> from transformers import {tokenizer_class}, {model_class} | |||
>>> import torch | |||
>>> tokenizer = {tokenizer_class}.from_pretrained('{checkpoint}') | |||
>>> model = {model_class}.from_pretrained('{checkpoint}') | |||
>>> inputs = tokenizer("Hello, my dog is cute", return_tensors="pt") | |||
>>> labels = torch.tensor([1] * inputs["input_ids"].size(1)).unsqueeze(0) # Batch size 1 | |||
>>> outputs = model(**inputs, labels=labels) | |||
>>> loss = outputs.loss | |||
>>> logits = outputs.logits | |||
""" | |||
PT_QUESTION_ANSWERING_SAMPLE = r""" | |||
Example:: | |||
>>> from transformers import {tokenizer_class}, {model_class} | |||
>>> import torch | |||
>>> tokenizer = {tokenizer_class}.from_pretrained('{checkpoint}') | |||
>>> model = {model_class}.from_pretrained('{checkpoint}') | |||
>>> question, text = "Who was Jim Henson?", "Jim Henson was a nice puppet" | |||
>>> inputs = tokenizer(question, text, return_tensors='pt') | |||
>>> start_positions = torch.tensor([1]) | |||
>>> end_positions = torch.tensor([3]) | |||
>>> outputs = model(**inputs, start_positions=start_positions, end_positions=end_positions) | |||
>>> loss = outputs.loss | |||
>>> start_scores = outputs.start_logits | |||
>>> end_scores = outputs.end_logits | |||
""" | |||
PT_SEQUENCE_CLASSIFICATION_SAMPLE = r""" | |||
Example:: | |||
>>> from transformers import {tokenizer_class}, {model_class} | |||
>>> import torch | |||
>>> tokenizer = {tokenizer_class}.from_pretrained('{checkpoint}') | |||
>>> model = {model_class}.from_pretrained('{checkpoint}') | |||
>>> inputs = tokenizer("Hello, my dog is cute", return_tensors="pt") | |||
>>> labels = torch.tensor([1]).unsqueeze(0) # Batch size 1 | |||
>>> outputs = model(**inputs, labels=labels) | |||
>>> loss = outputs.loss | |||
>>> logits = outputs.logits | |||
""" | |||
PT_MASKED_LM_SAMPLE = r""" | |||
Example:: | |||
>>> from transformers import {tokenizer_class}, {model_class} | |||
>>> import torch | |||
>>> tokenizer = {tokenizer_class}.from_pretrained('{checkpoint}') | |||
>>> model = {model_class}.from_pretrained('{checkpoint}') | |||
>>> inputs = tokenizer("The capital of France is {mask}.", return_tensors="pt") | |||
>>> labels = tokenizer("The capital of France is Paris.", return_tensors="pt")["input_ids"] | |||
>>> outputs = model(**inputs, labels=labels) | |||
>>> loss = outputs.loss | |||
>>> logits = outputs.logits | |||
""" | |||
PT_BASE_MODEL_SAMPLE = r""" | |||
Example:: | |||
>>> from transformers import {tokenizer_class}, {model_class} | |||
>>> import torch | |||
>>> tokenizer = {tokenizer_class}.from_pretrained('{checkpoint}') | |||
>>> model = {model_class}.from_pretrained('{checkpoint}') | |||
>>> inputs = tokenizer("Hello, my dog is cute", return_tensors="pt") | |||
>>> outputs = model(**inputs) | |||
>>> last_hidden_states = outputs.last_hidden_state | |||
""" | |||
PT_MULTIPLE_CHOICE_SAMPLE = r""" | |||
Example:: | |||
>>> from transformers import {tokenizer_class}, {model_class} | |||
>>> import torch | |||
>>> tokenizer = {tokenizer_class}.from_pretrained('{checkpoint}') | |||
>>> model = {model_class}.from_pretrained('{checkpoint}') | |||
>>> 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([prompt, prompt], [choice0, choice1], return_tensors='pt', padding=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 = outputs.loss | |||
>>> logits = outputs.logits | |||
""" | |||
PT_CAUSAL_LM_SAMPLE = r""" | |||
Example:: | |||
>>> import torch | |||
>>> from transformers import {tokenizer_class}, {model_class} | |||
>>> tokenizer = {tokenizer_class}.from_pretrained('{checkpoint}') | |||
>>> model = {model_class}.from_pretrained('{checkpoint}') | |||
>>> inputs = tokenizer("Hello, my dog is cute", return_tensors="pt") | |||
>>> outputs = model(**inputs, labels=inputs["input_ids"]) | |||
>>> loss = outputs.loss | |||
>>> logits = outputs.logits | |||
""" | |||
PT_SAMPLE_DOCSTRINGS = { | |||
"SequenceClassification": PT_SEQUENCE_CLASSIFICATION_SAMPLE, | |||
"QuestionAnswering": PT_QUESTION_ANSWERING_SAMPLE, | |||
"TokenClassification": PT_TOKEN_CLASSIFICATION_SAMPLE, | |||
"MultipleChoice": PT_MULTIPLE_CHOICE_SAMPLE, | |||
"MaskedLM": PT_MASKED_LM_SAMPLE, | |||
"LMHead": PT_CAUSAL_LM_SAMPLE, | |||
"BaseModel": PT_BASE_MODEL_SAMPLE, | |||
} | |||
def add_code_sample_docstrings( | |||
*docstr, tokenizer_class=None, checkpoint=None, output_type=None, config_class=None, mask=None, model_cls=None | |||
): | |||
def docstring_decorator(fn): | |||
# model_class defaults to function's class if not specified otherwise | |||
model_class = fn.__qualname__.split(".")[0] if model_cls is None else model_cls | |||
sample_docstrings = PT_SAMPLE_DOCSTRINGS | |||
doc_kwargs = dict(model_class=model_class, tokenizer_class=tokenizer_class, checkpoint=checkpoint) | |||
if "SequenceClassification" in model_class: | |||
code_sample = sample_docstrings["SequenceClassification"] | |||
elif "QuestionAnswering" in model_class: | |||
code_sample = sample_docstrings["QuestionAnswering"] | |||
elif "TokenClassification" in model_class: | |||
code_sample = sample_docstrings["TokenClassification"] | |||
elif "MultipleChoice" in model_class: | |||
code_sample = sample_docstrings["MultipleChoice"] | |||
elif "MaskedLM" in model_class or model_class in ["FlaubertWithLMHeadModel", "XLMWithLMHeadModel"]: | |||
doc_kwargs["mask"] = "[MASK]" if mask is None else mask | |||
code_sample = sample_docstrings["MaskedLM"] | |||
elif "LMHead" in model_class or "CausalLM" in model_class: | |||
code_sample = sample_docstrings["LMHead"] | |||
elif "Model" in model_class or "Encoder" in model_class: | |||
code_sample = sample_docstrings["BaseModel"] | |||
else: | |||
raise ValueError(f"Docstring can't be built for model {model_class}") | |||
output_doc = _prepare_output_docstrings(output_type, config_class) if output_type is not None else "" | |||
built_doc = code_sample.format(**doc_kwargs) | |||
fn.__doc__ = (fn.__doc__ or "") + "".join(docstr) + output_doc + built_doc | |||
return fn | |||
return docstring_decorator | |||
def replace_return_docstrings(output_type=None, config_class=None): | |||
def docstring_decorator(fn): | |||
docstrings = fn.__doc__ | |||
lines = docstrings.split("\n") | |||
i = 0 | |||
while i < len(lines) and re.search(r"^\s*Returns?:\s*$", lines[i]) is None: | |||
i += 1 | |||
if i < len(lines): | |||
lines[i] = _prepare_output_docstrings(output_type, config_class) | |||
docstrings = "\n".join(lines) | |||
else: | |||
raise ValueError( | |||
f"The function {fn} should have an empty 'Return:' or 'Returns:' in its docstring as placeholder, current docstring is:\n{docstrings}" | |||
) | |||
fn.__doc__ = docstrings | |||
return fn | |||
return docstring_decorator | |||
def is_remote_url(url_or_filename): | |||
parsed = urlparse(url_or_filename) | |||
return parsed.scheme in ("http", "https") | |||
def hf_bucket_url( | |||
model_id: str, filename: str, subfolder: Optional[str] = None, revision: Optional[str] = None, mirror=None | |||
) -> str: | |||
""" | |||
Resolve a model identifier, a file name, and an optional revision id, to a huggingface.co-hosted url, redirecting | |||
to Cloudfront (a Content Delivery Network, or CDN) for large files. | |||
Cloudfront is replicated over the globe so downloads are way faster for the end user (and it also lowers our | |||
bandwidth costs). | |||
Cloudfront aggressively caches files by default (default TTL is 24 hours), however this is not an issue here | |||
because we migrated to a git-based versioning system on huggingface.co, so we now store the files on S3/Cloudfront | |||
in a content-addressable way (i.e., the file name is its hash). Using content-addressable filenames means cache | |||
can't ever be stale. | |||
In terms of client-side caching from this library, we base our caching on the objects' ETag. An object' ETag is: | |||
its sha1 if stored in git, or its sha256 if stored in git-lfs. Files cached locally from transformers before v3.5.0 | |||
are not shared with those new files, because the cached file's name contains a hash of the url (which changed). | |||
""" | |||
if subfolder is not None: | |||
filename = f"{subfolder}/{filename}" | |||
if mirror: | |||
if mirror in ["tuna", "bfsu"]: | |||
raise ValueError("The Tuna and BFSU mirrors are no longer available. Try removing the mirror argument.") | |||
legacy_format = "/" not in model_id | |||
if legacy_format: | |||
return f"{mirror}/{model_id}-{filename}" | |||
else: | |||
return f"{mirror}/{model_id}/{filename}" | |||
if revision is None: | |||
revision = "main" | |||
return HUGGINGFACE_CO_PREFIX.format(model_id=model_id, revision=revision, filename=filename) | |||
def url_to_filename(url: str, etag: Optional[str] = None) -> str: | |||
""" | |||
Convert `url` into a hashed filename in a repeatable way. If `etag` is specified, append its hash to the url's, | |||
delimited by a period. If the url ends with .h5 (Keras HDF5 weights) adds '.h5' to the name so that TF 2.0 can | |||
identify it as a HDF5 file (see | |||
https://github.com/tensorflow/tensorflow/blob/00fad90125b18b80fe054de1055770cfb8fe4ba3/tensorflow/python/keras/engine/network.py#L1380) | |||
""" | |||
url_bytes = url.encode("utf-8") | |||
filename = sha256(url_bytes).hexdigest() | |||
if etag: | |||
etag_bytes = etag.encode("utf-8") | |||
filename += "." + sha256(etag_bytes).hexdigest() | |||
if url.endswith(".h5"): | |||
filename += ".h5" | |||
return filename | |||
def cached_path( | |||
url_or_filename, | |||
cache_dir=None, | |||
force_download=False, | |||
proxies=None, | |||
resume_download=False, | |||
user_agent: Union[Dict, str, None] = None, | |||
extract_compressed_file=False, | |||
force_extract=False, | |||
use_auth_token: Union[bool, str, None] = None, | |||
local_files_only=False, | |||
) -> Optional[str]: | |||
""" | |||
Given something that might be a URL (or might be a local path), determine which. If it's a URL, download the file | |||
and cache it, and return the path to the cached file. If it's already a local path, make sure the file exists and | |||
then return the path | |||
Args: | |||
cache_dir: specify a cache directory to save the file to (overwrite the default cache dir). | |||
force_download: if True, re-download the file even if it's already cached in the cache dir. | |||
resume_download: if True, resume the download if incompletely received file is found. | |||
user_agent: Optional string or dict that will be appended to the user-agent on remote requests. | |||
use_auth_token: Optional string or boolean to use as Bearer token for remote files. If True, | |||
will get token from ~/.huggingface. | |||
extract_compressed_file: if True and the path point to a zip or tar file, extract the compressed | |||
file in a folder along the archive. | |||
force_extract: if True when extract_compressed_file is True and the archive was already extracted, | |||
re-extract the archive and override the folder where it was extracted. | |||
Return: | |||
Local path (string) of file or if networking is off, last version of file cached on disk. | |||
Raises: | |||
In case of non-recoverable file (non-existent or inaccessible url + no cache on disk). | |||
""" | |||
if cache_dir is None: | |||
cache_dir = TRANSFORMERS_CACHE | |||
if isinstance(url_or_filename, Path): | |||
url_or_filename = str(url_or_filename) | |||
if isinstance(cache_dir, Path): | |||
cache_dir = str(cache_dir) | |||
if is_offline_mode() and not local_files_only: | |||
logger.info("Offline mode: forcing local_files_only=True") | |||
local_files_only = True | |||
if is_remote_url(url_or_filename): | |||
# URL, so get it from the cache (downloading if necessary) | |||
output_path = get_from_cache( | |||
url_or_filename, | |||
cache_dir=cache_dir, | |||
force_download=force_download, | |||
proxies=proxies, | |||
resume_download=resume_download, | |||
user_agent=user_agent, | |||
use_auth_token=use_auth_token, | |||
local_files_only=local_files_only, | |||
) | |||
elif os.path.exists(url_or_filename): | |||
# File, and it exists. | |||
output_path = url_or_filename | |||
elif urlparse(url_or_filename).scheme == "": | |||
# File, but it doesn't exist. | |||
raise EnvironmentError(f"file {url_or_filename} not found") | |||
else: | |||
# Something unknown | |||
raise ValueError(f"unable to parse {url_or_filename} as a URL or as a local path") | |||
if extract_compressed_file: | |||
if not is_zipfile(output_path) and not tarfile.is_tarfile(output_path): | |||
return output_path | |||
# Path where we extract compressed archives | |||
# We avoid '.' in dir name and add "-extracted" at the end: "./model.zip" => "./model-zip-extracted/" | |||
output_dir, output_file = os.path.split(output_path) | |||
output_extract_dir_name = output_file.replace(".", "-") + "-extracted" | |||
output_path_extracted = os.path.join(output_dir, output_extract_dir_name) | |||
if os.path.isdir(output_path_extracted) and os.listdir(output_path_extracted) and not force_extract: | |||
return output_path_extracted | |||
# Prevent parallel extractions | |||
lock_path = output_path + ".lock" | |||
with filelock(lock_path): | |||
shutil.rmtree(output_path_extracted, ignore_errors=True) | |||
os.makedirs(output_path_extracted) | |||
if is_zipfile(output_path): | |||
with ZipFile(output_path, "r") as zip_file: | |||
zip_file.extractall(output_path_extracted) | |||
zip_file.close() | |||
elif tarfile.is_tarfile(output_path): | |||
tar_file = tarfile.open(output_path) | |||
tar_file.extractall(output_path_extracted) | |||
tar_file.close() | |||
else: | |||
raise EnvironmentError(f"Archive format of {output_path} could not be identified") | |||
return output_path_extracted | |||
return output_path | |||
def define_sagemaker_information(): | |||
try: | |||
instance_data = requests.get(os.environ["ECS_CONTAINER_METADATA_URI"]).json() | |||
dlc_container_used = instance_data["Image"] | |||
dlc_tag = instance_data["Image"].split(":")[1] | |||
except Exception: | |||
dlc_container_used = None | |||
dlc_tag = None | |||
sagemaker_params = json.loads(os.getenv("SM_FRAMEWORK_PARAMS", "{}")) | |||
runs_distributed_training = True if "sagemaker_distributed_dataparallel_enabled" in sagemaker_params else False | |||
account_id = os.getenv("TRAINING_JOB_ARN").split(":")[4] if "TRAINING_JOB_ARN" in os.environ else None | |||
sagemaker_object = { | |||
"sm_framework": os.getenv("SM_FRAMEWORK_MODULE", None), | |||
"sm_region": os.getenv("AWS_REGION", None), | |||
"sm_number_gpu": os.getenv("SM_NUM_GPUS", 0), | |||
"sm_number_cpu": os.getenv("SM_NUM_CPUS", 0), | |||
"sm_distributed_training": runs_distributed_training, | |||
"sm_deep_learning_container": dlc_container_used, | |||
"sm_deep_learning_container_tag": dlc_tag, | |||
"sm_account_id": account_id, | |||
} | |||
return sagemaker_object | |||
def http_user_agent(user_agent: Union[Dict, str, None] = None) -> str: | |||
""" | |||
Formats a user-agent string with basic info about a request. | |||
""" | |||
ua = f"transformers/{__version__}; python/{sys.version.split()[0]}; session_id/{SESSION_ID}" | |||
if _NEED_IMPORT_TORCH: | |||
ua += f"; torch/{_torch_version}" | |||
if DISABLE_TELEMETRY: | |||
return ua + "; telemetry/off" | |||
if is_training_run_on_sagemaker(): | |||
ua += "; " + "; ".join(f"{k}/{v}" for k, v in define_sagemaker_information().items()) | |||
# CI will set this value to True | |||
if os.environ.get("TRANSFORMERS_IS_CI", "").upper() in ENV_VARS_TRUE_VALUES: | |||
ua += "; is_ci/true" | |||
if isinstance(user_agent, dict): | |||
ua += "; " + "; ".join(f"{k}/{v}" for k, v in user_agent.items()) | |||
elif isinstance(user_agent, str): | |||
ua += "; " + user_agent | |||
return ua | |||
def http_get(url: str, temp_file: BinaryIO, proxies=None, resume_size=0, headers: Optional[Dict[str, str]] = None): | |||
""" | |||
Download remote file. Do not gobble up errors. | |||
""" | |||
headers = copy.deepcopy(headers) | |||
if resume_size > 0: | |||
headers["Range"] = f"bytes={resume_size}-" | |||
r = requests.get(url, stream=True, proxies=proxies, headers=headers) | |||
r.raise_for_status() | |||
content_length = r.headers.get("Content-Length") | |||
total = resume_size + int(content_length) if content_length is not None else None | |||
# progress = tqdm( | |||
# unit="B", | |||
# unit_scale=True, | |||
# unit_divisor=1024, | |||
# total=total, | |||
# initial=resume_size, | |||
# desc="Downloading", | |||
# disable=bool(logging.get_verbosity() == logging.NOTSET), | |||
# ) | |||
for chunk in r.iter_content(chunk_size=1024): | |||
if chunk: # filter out keep-alive new chunks | |||
# progress.update(len(chunk)) | |||
temp_file.write(chunk) | |||
# progress.close() | |||
def get_from_cache( | |||
url: str, | |||
cache_dir=None, | |||
force_download=False, | |||
proxies=None, | |||
etag_timeout=10, | |||
resume_download=False, | |||
user_agent: Union[Dict, str, None] = None, | |||
use_auth_token: Union[bool, str, None] = None, | |||
local_files_only=False, | |||
) -> Optional[str]: | |||
""" | |||
Given a URL, look for the corresponding file in the local cache. If it's not there, download it. Then return the | |||
path to the cached file. | |||
Return: | |||
Local path (string) of file or if networking is off, last version of file cached on disk. | |||
Raises: | |||
In case of non-recoverable file (non-existent or inaccessible url + no cache on disk). | |||
""" | |||
if cache_dir is None: | |||
cache_dir = TRANSFORMERS_CACHE | |||
if isinstance(cache_dir, Path): | |||
cache_dir = str(cache_dir) | |||
os.makedirs(cache_dir, exist_ok=True) | |||
headers = {"user-agent": http_user_agent(user_agent)} | |||
if isinstance(use_auth_token, str): | |||
headers["authorization"] = f"Bearer {use_auth_token}" | |||
elif use_auth_token: | |||
raise RuntimeError("`use_auth_token=True` is not supported in FastNLP now") | |||
# token = HfFolder.get_token() | |||
# if token is None: | |||
# raise EnvironmentError("You specified use_auth_token=True, but a huggingface token was not found.") | |||
# headers["authorization"] = f"Bearer {token}" | |||
url_to_download = url | |||
etag = None | |||
if not local_files_only: | |||
try: | |||
r = requests.head(url, headers=headers, allow_redirects=False, proxies=proxies, timeout=etag_timeout) | |||
r.raise_for_status() | |||
etag = r.headers.get("X-Linked-Etag") or r.headers.get("ETag") | |||
# We favor a custom header indicating the etag of the linked resource, and | |||
# we fallback to the regular etag header. | |||
# If we don't have any of those, raise an error. | |||
if etag is None: | |||
raise OSError( | |||
"Distant resource does not have an ETag, we won't be able to reliably ensure reproducibility." | |||
) | |||
# In case of a redirect, | |||
# save an extra redirect on the request.get call, | |||
# and ensure we download the exact atomic version even if it changed | |||
# between the HEAD and the GET (unlikely, but hey). | |||
if 300 <= r.status_code <= 399: | |||
url_to_download = r.headers["Location"] | |||
except (requests.exceptions.SSLError, requests.exceptions.ProxyError): | |||
# Actually raise for those subclasses of ConnectionError | |||
raise | |||
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): | |||
# Otherwise, our Internet connection is down. | |||
# etag is None | |||
pass | |||
filename = url_to_filename(url, etag) | |||
# get cache path to put the file | |||
cache_path = os.path.join(cache_dir, filename) | |||
# etag is None == we don't have a connection or we passed local_files_only. | |||
# try to get the last downloaded one | |||
if etag is None: | |||
if os.path.exists(cache_path): | |||
return cache_path | |||
else: | |||
matching_files = [ | |||
file | |||
for file in fnmatch.filter(os.listdir(cache_dir), filename.split(".")[0] + ".*") | |||
if not file.endswith(".json") and not file.endswith(".lock") | |||
] | |||
if len(matching_files) > 0: | |||
return os.path.join(cache_dir, matching_files[-1]) | |||
else: | |||
# If files cannot be found and local_files_only=True, | |||
# the models might've been found if local_files_only=False | |||
# Notify the user about that | |||
if local_files_only: | |||
raise FileNotFoundError( | |||
"Cannot find the requested files in the cached path and outgoing traffic has been" | |||
" disabled. To enable model look-ups and downloads online, set 'local_files_only'" | |||
" to False." | |||
) | |||
else: | |||
raise ValueError( | |||
"Connection error, and we cannot find the requested files in the cached path." | |||
" Please try again or make sure your Internet connection is on." | |||
) | |||
# From now on, etag is not None. | |||
if os.path.exists(cache_path) and not force_download: | |||
return cache_path | |||
# Prevent parallel downloads of the same file with a lock. | |||
lock_path = cache_path + ".lock" | |||
with filelock(lock_path): | |||
# If the download just completed while the lock was activated. | |||
if os.path.exists(cache_path) and not force_download: | |||
# Even if returning early like here, the lock will be released. | |||
return cache_path | |||
if resume_download: | |||
incomplete_path = cache_path + ".incomplete" | |||
@contextmanager | |||
def _resumable_file_manager() -> "io.BufferedWriter": | |||
with open(incomplete_path, "ab") as f: | |||
yield f | |||
temp_file_manager = _resumable_file_manager | |||
if os.path.exists(incomplete_path): | |||
resume_size = os.stat(incomplete_path).st_size | |||
else: | |||
resume_size = 0 | |||
else: | |||
temp_file_manager = partial(tempfile.NamedTemporaryFile, mode="wb", dir=cache_dir, delete=False) | |||
resume_size = 0 | |||
# Download to temporary file, then copy to cache dir once finished. | |||
# Otherwise you get corrupt cache entries if the download gets interrupted. | |||
with temp_file_manager() as temp_file: | |||
logger.info(f"{url} not found in cache or force_download set to True, downloading to {temp_file.name}") | |||
http_get(url_to_download, temp_file, proxies=proxies, resume_size=resume_size, headers=headers) | |||
logger.info(f"storing {url} in cache at {cache_path}") | |||
os.replace(temp_file.name, cache_path) | |||
# NamedTemporaryFile creates a file with hardwired 0600 perms (ignoring umask), so fixing it. | |||
umask = os.umask(0o666) | |||
os.umask(umask) | |||
os.chmod(cache_path, 0o666 & ~umask) | |||
logger.info(f"creating metadata file for {cache_path}") | |||
meta = {"url": url, "etag": etag} | |||
meta_path = cache_path + ".json" | |||
with open(meta_path, "w") as meta_file: | |||
json.dump(meta, meta_file) | |||
return cache_path | |||
def is_torch_fx_available(): | |||
return _TORCH_GREATER_EQUAL_1_8 and _compare_version("torch", operator.lt, "1.9.0") | |||
def is_torch_fx_proxy(x): | |||
if is_torch_fx_available(): | |||
import torch.fx | |||
return isinstance(x, torch.fx.Proxy) | |||
return False | |||
def is_sentencepiece_available(): | |||
return importlib.util.find_spec("sentencepiece") is not None | |||
def is_tokenizers_available(): | |||
return importlib.util.find_spec("tokenizers") is not None | |||
def is_tensor(x): | |||
""" | |||
Tests if ``x`` is a :obj:`torch.Tensor`, :obj:`tf.Tensor`, obj:`jaxlib.xla_extension.DeviceArray` or | |||
:obj:`np.ndarray`. | |||
""" | |||
if is_torch_fx_proxy(x): | |||
return True | |||
if isinstance(x, torch.Tensor): | |||
return True | |||
return isinstance(x, np.ndarray) | |||
def to_py_obj(obj): | |||
""" | |||
Convert a TensorFlow tensor, PyTorch tensor, Numpy array or python list to a python list. | |||
""" | |||
if isinstance(obj, (dict, UserDict)): | |||
return {k: to_py_obj(v) for k, v in obj.items()} | |||
elif isinstance(obj, (list, tuple)): | |||
return [to_py_obj(o) for o in obj] | |||
elif _NEED_IMPORT_TORCH and _is_torch(obj): | |||
return obj.detach().cpu().tolist() | |||
elif isinstance(obj, np.ndarray): | |||
return obj.tolist() | |||
else: | |||
return obj | |||
def _is_numpy(x): | |||
return isinstance(x, np.ndarray) | |||
def _is_torch(x): | |||
import torch | |||
return isinstance(x, torch.Tensor) | |||
def _is_torch_device(x): | |||
import torch | |||
return isinstance(x, torch.device) | |||
class ModelOutput(OrderedDict): | |||
""" | |||
Base class for all model outputs as dataclass. Has a ``__getitem__`` that allows indexing by integer or slice (like | |||
a tuple) or strings (like a dictionary) that will ignore the ``None`` attributes. Otherwise behaves like a regular | |||
python dictionary. | |||
.. warning:: | |||
You can't unpack a :obj:`ModelOutput` directly. Use the :meth:`~transformers.file_utils.ModelOutput.to_tuple` | |||
method to convert it to a tuple before. | |||
""" | |||
def __post_init__(self): | |||
class_fields = fields(self) | |||
# Safety and consistency checks | |||
assert len(class_fields), f"{self.__class__.__name__} has no fields." | |||
assert all( | |||
field.default is None for field in class_fields[1:] | |||
), f"{self.__class__.__name__} should not have more than one required field." | |||
first_field = getattr(self, class_fields[0].name) | |||
other_fields_are_none = all(getattr(self, field.name) is None for field in class_fields[1:]) | |||
if other_fields_are_none and not is_tensor(first_field): | |||
if isinstance(first_field, dict): | |||
iterator = first_field.items() | |||
first_field_iterator = True | |||
else: | |||
try: | |||
iterator = iter(first_field) | |||
first_field_iterator = True | |||
except TypeError: | |||
first_field_iterator = False | |||
# if we provided an iterator as first field and the iterator is a (key, value) iterator | |||
# set the associated fields | |||
if first_field_iterator: | |||
for element in iterator: | |||
if ( | |||
not isinstance(element, (list, tuple)) | |||
or not len(element) == 2 | |||
or not isinstance(element[0], str) | |||
): | |||
break | |||
setattr(self, element[0], element[1]) | |||
if element[1] is not None: | |||
self[element[0]] = element[1] | |||
elif first_field is not None: | |||
self[class_fields[0].name] = first_field | |||
else: | |||
for field in class_fields: | |||
v = getattr(self, field.name) | |||
if v is not None: | |||
self[field.name] = v | |||
def __delitem__(self, *args, **kwargs): | |||
raise Exception(f"You cannot use ``__delitem__`` on a {self.__class__.__name__} instance.") | |||
def setdefault(self, *args, **kwargs): | |||
raise Exception(f"You cannot use ``setdefault`` on a {self.__class__.__name__} instance.") | |||
def pop(self, *args, **kwargs): | |||
raise Exception(f"You cannot use ``pop`` on a {self.__class__.__name__} instance.") | |||
def update(self, *args, **kwargs): | |||
raise Exception(f"You cannot use ``update`` on a {self.__class__.__name__} instance.") | |||
def __getitem__(self, k): | |||
if isinstance(k, str): | |||
inner_dict = {k: v for (k, v) in self.items()} | |||
return inner_dict[k] | |||
else: | |||
return self.to_tuple()[k] | |||
def __setattr__(self, name, value): | |||
if name in self.keys() and value is not None: | |||
# Don't call self.__setitem__ to avoid recursion errors | |||
super().__setitem__(name, value) | |||
super().__setattr__(name, value) | |||
def __setitem__(self, key, value): | |||
# Will raise a KeyException if needed | |||
super().__setitem__(key, value) | |||
# Don't call self.__setattr__ to avoid recursion errors | |||
super().__setattr__(key, value) | |||
def to_tuple(self) -> Tuple[Any]: | |||
""" | |||
Convert self to a tuple containing all the attributes/keys that are not ``None``. | |||
""" | |||
return tuple(self[k] for k in self.keys()) | |||
class ExplicitEnum(Enum): | |||
""" | |||
Enum with more explicit error message for missing values. | |||
""" | |||
@classmethod | |||
def _missing_(cls, value): | |||
raise ValueError( | |||
f"{value} is not a valid {cls.__name__}, please select one of {list(cls._value2member_map_.keys())}" | |||
) | |||
class PaddingStrategy(ExplicitEnum): | |||
""" | |||
Possible values for the ``padding`` argument in :meth:`PreTrainedTokenizerBase.__call__`. Useful for tab-completion | |||
in an IDE. | |||
""" | |||
LONGEST = "longest" | |||
MAX_LENGTH = "max_length" | |||
DO_NOT_PAD = "do_not_pad" | |||
class TensorType(ExplicitEnum): | |||
""" | |||
Possible values for the ``return_tensors`` argument in :meth:`PreTrainedTokenizerBase.__call__`. Useful for | |||
tab-completion in an IDE. | |||
""" | |||
PYTORCH = "pt" | |||
NUMPY = "np" |
@@ -0,0 +1,393 @@ | |||
# coding=utf-8 | |||
# Copyright 2020 The HuggingFace Inc. team | |||
# | |||
# 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. | |||
from abc import ABC, abstractmethod | |||
from collections import UserDict | |||
from typing import Optional, Tuple | |||
from .file_utils import add_start_docstrings | |||
from fastNLP.envs.imports import _NEED_IMPORT_TORCH | |||
from fastNLP.core.log import logger | |||
if _NEED_IMPORT_TORCH: | |||
import torch | |||
PROCESS_INPUTS_DOCSTRING = r""" | |||
Args: | |||
input_ids (:obj:`torch.LongTensor` of shape :obj:`(batch_size * num_beams, sequence_length)`): | |||
Indices of input sequence tokens in the vocabulary. | |||
Indices can be obtained using any class inheriting from :class:`~transformers.PreTrainedTokenizer`. See | |||
:meth:`transformers.PreTrainedTokenizer.encode` and :meth:`transformers.PreTrainedTokenizer.__call__` for | |||
details. | |||
`What are input IDs? <../glossary.html#input-ids>`__ | |||
next_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, 2 * num_beams)`): | |||
Current scores of the top :obj:`2 * num_beams` non-finished beam hypotheses. | |||
next_tokens (:obj:`torch.LongTensor` of shape :obj:`(batch_size, 2 * num_beams)`): | |||
:obj:`input_ids` of the tokens corresponding to the top :obj:`2 * num_beams` non-finished beam hypotheses. | |||
next_indices (:obj:`torch.LongTensor` of shape :obj:`(batch_size, 2 * num_beams)`): | |||
Beam indices indicating to which beam hypothesis the :obj:`next_tokens` correspond. | |||
pad_token_id (:obj:`int`, `optional`): | |||
The id of the `padding` token. | |||
eos_token_id (:obj:`int`, `optional`): | |||
The id of the `end-of-sequence` token. | |||
Return: | |||
:obj:`UserDict`: A dictionary composed of the fields as defined above: | |||
- **next_beam_scores** (:obj:`torch.FloatTensor` of shape :obj:`(batch_size * num_beams)`) -- Updated | |||
scores of all non-finished beams. | |||
- **next_beam_tokens** (:obj:`torch.FloatTensor` of shape :obj:`(batch_size * num_beams)`) -- Next tokens | |||
to be added to the non-finished beam_hypotheses. | |||
- **next_beam_indices** (:obj:`torch.FloatTensor` of shape :obj:`(batch_size * num_beams)`) -- Beam indices | |||
indicating to which beam the next tokens shall be added. | |||
""" | |||
FINALIZE_INPUTS_DOCSTRING = r""" | |||
Args: | |||
input_ids (:obj:`torch.LongTensor` of shape :obj:`(batch_size * num_beams, sequence_length)`): | |||
Indices of input sequence tokens in the vocabulary. | |||
Indices can be obtained using any class inheriting from :class:`~transformers.PreTrainedTokenizer`. See | |||
:meth:`transformers.PreTrainedTokenizer.encode` and :meth:`transformers.PreTrainedTokenizer.__call__` for | |||
details. | |||
`What are input IDs? <../glossary.html#input-ids>`__ | |||
final_beam_scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size * num_beams)`): | |||
The final scores of all non-finished beams. | |||
final_beam_tokens (:obj:`torch.FloatTensor` of shape :obj:`(batch_size * num_beams)`): | |||
The last tokens to be added to the non-finished beam_hypotheses. | |||
final_beam_indices (:obj:`torch.FloatTensor` of shape :obj:`(batch_size * num_beams)`): | |||
The beam indices indicating to which beam the :obj:`final_beam_tokens` shall be added. | |||
pad_token_id (:obj:`int`, `optional`): | |||
The id of the `padding` token. | |||
eos_token_id (:obj:`int`, `optional`): | |||
The id of the `end-of-sequence` token. | |||
Return: | |||
:obj:`torch.LongTensor` of shape :obj:`(batch_size * num_return_sequences, sequence_length)`: The generated | |||
sequences. The second dimension (sequence_length) is either equal to :obj:`max_length` or shorter if all | |||
batches finished early due to the :obj:`eos_token_id`. | |||
""" | |||
class BeamScorer(ABC): | |||
""" | |||
Abstract base class for all beam scorers that are used for :meth:`~transformers.PreTrainedModel.beam_search` and | |||
:meth:`~transformers.PreTrainedModel.beam_sample`. | |||
""" | |||
@abstractmethod | |||
@add_start_docstrings(PROCESS_INPUTS_DOCSTRING) | |||
def process( | |||
self, | |||
input_ids: "torch.LongTensor", | |||
next_scores: "torch.FloatTensor", | |||
next_tokens: "torch.LongTensor", | |||
next_indices: "torch.LongTensor", | |||
**kwargs | |||
) -> Tuple["torch.Tensor"]: | |||
raise NotImplementedError("This is an abstract method.") | |||
@abstractmethod | |||
@add_start_docstrings(FINALIZE_INPUTS_DOCSTRING) | |||
def finalize( | |||
self, | |||
input_ids: "torch.LongTensor", | |||
next_scores: "torch.FloatTensor", | |||
next_tokens: "torch.LongTensor", | |||
next_indices: "torch.LongTensor", | |||
max_length: int, | |||
**kwargs | |||
) -> "torch.LongTensor": | |||
raise NotImplementedError("This is an abstract method.") | |||
class BeamSearchScorer(BeamScorer): | |||
r""" | |||
:class:`transformers.BeamScorer` implementing standard beam search decoding. | |||
Adapted in part from `Facebook's XLM beam search code | |||
<https://github.com/facebookresearch/XLM/blob/9e6f6814d17be4fe5b15f2e6c43eb2b2d76daeb4/src/model/transformer.py#L529>`__. | |||
Reference for the diverse beam search algorithm and implementation `Ashwin Kalyan's DBS implementation | |||
<https://github.com/ashwinkalyan/dbs/blob/master/dbs/beam_utils.lua>`__ | |||
Args: | |||
batch_size (:obj:`int`): | |||
Batch Size of :obj:`input_ids` for which standard beam search decoding is run in parallel. | |||
max_length (:obj:`int`): | |||
The maximum length of the sequence to be generated. | |||
num_beams (:obj:`int`): | |||
Number of beams for beam search. | |||
device (:obj:`torch.device`): | |||
Defines the device type (*e.g.*, :obj:`"cpu"` or :obj:`"cuda"`) on which this instance of | |||
:obj:`BeamSearchScorer` will be allocated. | |||
length_penalty (:obj:`float`, `optional`, defaults to 1.0): | |||
Exponential penalty to the length. 1.0 means no penalty. Set to values < 1.0 in order to encourage the | |||
model to generate shorter sequences, to a value > 1.0 in order to encourage the model to produce longer | |||
sequences. | |||
do_early_stopping (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether to stop the beam search when at least ``num_beams`` sentences are finished per batch or not. | |||
num_beam_hyps_to_keep (:obj:`int`, `optional`, defaults to 1): | |||
The number of beam hypotheses that shall be returned upon calling | |||
:meth:`~transformer.BeamSearchScorer.finalize`. | |||
num_beam_groups (:obj:`int`): | |||
Number of groups to divide :obj:`num_beams` into in order to ensure diversity among different groups of | |||
beams. See `this paper <https://arxiv.org/pdf/1610.02424.pdf>`__ for more details. | |||
""" | |||
def __init__( | |||
self, | |||
batch_size: int, | |||
num_beams: int, | |||
device: "torch.device", | |||
length_penalty: Optional[float] = 1.0, | |||
do_early_stopping: Optional[bool] = False, | |||
num_beam_hyps_to_keep: Optional[int] = 1, | |||
num_beam_groups: Optional[int] = 1, | |||
**kwargs, | |||
): | |||
self.num_beams = num_beams | |||
self.device = device | |||
self.length_penalty = length_penalty | |||
self.do_early_stopping = do_early_stopping | |||
self.num_beam_hyps_to_keep = num_beam_hyps_to_keep | |||
self.num_beam_groups = num_beam_groups | |||
self.group_size = self.num_beams // self.num_beam_groups | |||
self._is_init = False | |||
self._beam_hyps = [ | |||
BeamHypotheses( | |||
num_beams=self.num_beams, | |||
length_penalty=self.length_penalty, | |||
early_stopping=self.do_early_stopping, | |||
) | |||
for _ in range(batch_size) | |||
] | |||
self._done = torch.tensor([False for _ in range(batch_size)], dtype=torch.bool, device=self.device) | |||
if not isinstance(num_beams, int) or num_beams <= 1: | |||
raise ValueError( | |||
f"`num_beams` has to be an integer strictly greater than 1, but is {num_beams}. For `num_beams` == 1, one should make use of `greedy_search` instead." | |||
) | |||
if not isinstance(num_beam_groups, int) or (num_beam_groups > num_beams) or (num_beams % num_beam_groups != 0): | |||
raise ValueError( | |||
f"`num_beam_groups` has to be an integer smaller or equal than `num_beams` and `num_beams` " | |||
f"has to be divisible by `num_beam_groups`, but is {num_beam_groups} with `num_beams` being {num_beams}." | |||
) | |||
if "max_length" in kwargs: | |||
logger.warn( | |||
"Passing `max_length` to BeamSearchScorer is deprecated and has no effect." | |||
"`max_length` should be passed directly to `beam_search(...)`, `beam_sample(...)`" | |||
",or `group_beam_search(...)`." | |||
) | |||
@property | |||
def is_done(self) -> bool: | |||
return self._done.all() | |||
def process( | |||
self, | |||
input_ids: "torch.LongTensor", | |||
next_scores: "torch.FloatTensor", | |||
next_tokens: "torch.LongTensor", | |||
next_indices: "torch.LongTensor", | |||
pad_token_id: Optional[int] = None, | |||
eos_token_id: Optional[int] = None, | |||
) -> Tuple["torch.Tensor"]: | |||
cur_len = input_ids.shape[-1] | |||
batch_size = len(self._beam_hyps) | |||
assert batch_size == (input_ids.shape[0] // self.group_size) | |||
device = input_ids.device | |||
next_beam_scores = torch.zeros((batch_size, self.group_size), dtype=next_scores.dtype, device=device) | |||
next_beam_tokens = torch.zeros((batch_size, self.group_size), dtype=next_tokens.dtype, device=device) | |||
next_beam_indices = torch.zeros((batch_size, self.group_size), dtype=next_indices.dtype, device=device) | |||
for batch_idx, beam_hyp in enumerate(self._beam_hyps): | |||
if self._done[batch_idx]: | |||
assert ( | |||
len(beam_hyp) >= self.num_beams | |||
), f"Batch can only be done if at least {self.num_beams} beams have been generated" | |||
assert ( | |||
eos_token_id is not None and pad_token_id is not None | |||
), "generated beams >= num_beams -> eos_token_id and pad_token have to be defined" | |||
# pad the batch | |||
next_beam_scores[batch_idx, :] = 0 | |||
next_beam_tokens[batch_idx, :] = pad_token_id | |||
next_beam_indices[batch_idx, :] = 0 | |||
continue | |||
# next tokens for this sentence | |||
beam_idx = 0 | |||
for beam_token_rank, (next_token, next_score, next_index) in enumerate( | |||
zip(next_tokens[batch_idx], next_scores[batch_idx], next_indices[batch_idx]) | |||
): | |||
batch_beam_idx = batch_idx * self.group_size + next_index | |||
# add to generated hypotheses if end of sentence | |||
if (eos_token_id is not None) and (next_token.item() == eos_token_id): | |||
# if beam_token does not belong to top num_beams tokens, it should not be added | |||
is_beam_token_worse_than_top_num_beams = beam_token_rank >= self.group_size | |||
if is_beam_token_worse_than_top_num_beams: | |||
continue | |||
beam_hyp.add( | |||
input_ids[batch_beam_idx].clone(), | |||
next_score.item(), | |||
) | |||
else: | |||
# add next predicted token since it is not eos_token | |||
next_beam_scores[batch_idx, beam_idx] = next_score | |||
next_beam_tokens[batch_idx, beam_idx] = next_token | |||
next_beam_indices[batch_idx, beam_idx] = batch_beam_idx | |||
beam_idx += 1 | |||
# once the beam for next step is full, don't add more tokens to it. | |||
if beam_idx == self.group_size: | |||
break | |||
if beam_idx < self.group_size: | |||
raise ValueError( | |||
f"At most {self.group_size} tokens in {next_tokens[batch_idx]} can be equal to `eos_token_id: {eos_token_id}`. Make sure {next_tokens[batch_idx]} are corrected." | |||
) | |||
# Check if we are done so that we can save a pad step if all(done) | |||
self._done[batch_idx] = self._done[batch_idx] or beam_hyp.is_done( | |||
next_scores[batch_idx].max().item(), cur_len | |||
) | |||
return UserDict( | |||
{ | |||
"next_beam_scores": next_beam_scores.view(-1), | |||
"next_beam_tokens": next_beam_tokens.view(-1), | |||
"next_beam_indices": next_beam_indices.view(-1), | |||
} | |||
) | |||
def finalize( | |||
self, | |||
input_ids: "torch.LongTensor", | |||
final_beam_scores: "torch.FloatTensor", | |||
final_beam_tokens: "torch.LongTensor", | |||
final_beam_indices: "torch.LongTensor", | |||
max_length: int, | |||
pad_token_id: Optional[int] = None, | |||
eos_token_id: Optional[int] = None, | |||
) -> Tuple["torch.LongTensor"]: | |||
batch_size = len(self._beam_hyps) | |||
# finalize all open beam hypotheses and add to generated hypotheses | |||
for batch_idx, beam_hyp in enumerate(self._beam_hyps): | |||
if self._done[batch_idx]: | |||
continue | |||
# all open beam hypotheses are added to the beam hypothesis | |||
# beam hypothesis class automatically keeps the best beams | |||
for beam_id in range(self.num_beams): | |||
batch_beam_idx = batch_idx * self.num_beams + beam_id | |||
final_score = final_beam_scores[batch_beam_idx].item() | |||
final_tokens = input_ids[batch_beam_idx] | |||
beam_hyp.add(final_tokens, final_score) | |||
# select the best hypotheses | |||
sent_lengths = input_ids.new(batch_size * self.num_beam_hyps_to_keep) | |||
best = [] | |||
best_scores = torch.zeros(batch_size * self.num_beam_hyps_to_keep, device=self.device, dtype=torch.float32) | |||
# retrieve best hypotheses | |||
for i, beam_hyp in enumerate(self._beam_hyps): | |||
sorted_hyps = sorted(beam_hyp.beams, key=lambda x: x[0]) | |||
for j in range(self.num_beam_hyps_to_keep): | |||
best_hyp_tuple = sorted_hyps.pop() | |||
best_score = best_hyp_tuple[0] | |||
best_hyp = best_hyp_tuple[1] | |||
sent_lengths[self.num_beam_hyps_to_keep * i + j] = len(best_hyp) | |||
# append to lists | |||
best.append(best_hyp) | |||
best_scores[i * self.num_beam_hyps_to_keep + j] = best_score | |||
# prepare for adding eos | |||
sent_max_len = min(sent_lengths.max().item() + 1, max_length) | |||
decoded: "torch.LongTensor" = input_ids.new(batch_size * self.num_beam_hyps_to_keep, sent_max_len) | |||
# shorter batches are padded if needed | |||
if sent_lengths.min().item() != sent_lengths.max().item(): | |||
assert pad_token_id is not None, "`pad_token_id` has to be defined" | |||
decoded.fill_(pad_token_id) | |||
# fill with hypotheses and eos_token_id if the latter fits in | |||
for i, hypo in enumerate(best): | |||
decoded[i, : sent_lengths[i]] = hypo | |||
if sent_lengths[i] < max_length: | |||
decoded[i, sent_lengths[i]] = eos_token_id | |||
return UserDict( | |||
{ | |||
"sequences": decoded, | |||
"sequence_scores": best_scores, | |||
} | |||
) | |||
class BeamHypotheses: | |||
def __init__(self, num_beams: int, length_penalty: float, early_stopping: bool): | |||
""" | |||
Initialize n-best list of hypotheses. | |||
""" | |||
self.length_penalty = length_penalty | |||
self.early_stopping = early_stopping | |||
self.num_beams = num_beams | |||
self.beams = [] | |||
self.worst_score = 1e9 | |||
def __len__(self): | |||
""" | |||
Number of hypotheses in the list. | |||
""" | |||
return len(self.beams) | |||
def add(self, hyp: "torch.LongTensor", sum_logprobs: float): | |||
""" | |||
Add a new hypothesis to the list. | |||
""" | |||
score = sum_logprobs / (hyp.shape[-1] ** self.length_penalty) | |||
if len(self) < self.num_beams or score > self.worst_score: | |||
self.beams.append((score, hyp)) | |||
if len(self) > self.num_beams: | |||
sorted_next_scores = sorted([(s, idx) for idx, (s, _) in enumerate(self.beams)]) | |||
del self.beams[sorted_next_scores[0][1]] | |||
self.worst_score = sorted_next_scores[1][0] | |||
else: | |||
self.worst_score = min(score, self.worst_score) | |||
def is_done(self, best_sum_logprobs: float, cur_len: int) -> bool: | |||
""" | |||
If there are enough hypotheses and that none of the hypotheses being generated can become better than the worst | |||
one in the heap, then we are done with this sentence. | |||
""" | |||
if len(self) < self.num_beams: | |||
return False | |||
elif self.early_stopping: | |||
return True | |||
else: | |||
cur_score = best_sum_logprobs / cur_len ** self.length_penalty | |||
ret = self.worst_score >= cur_score | |||
return ret |
@@ -0,0 +1,618 @@ | |||
# coding=utf-8 | |||
# Copyright 2020 The HuggingFace Inc. team | |||
# | |||
# 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 inspect | |||
import math | |||
from abc import ABC | |||
from typing import Callable, Iterable, List, Optional | |||
import numpy as np | |||
from .file_utils import add_start_docstrings | |||
from fastNLP.envs.imports import _NEED_IMPORT_TORCH | |||
from fastNLP.core.log import logger | |||
if _NEED_IMPORT_TORCH: | |||
import torch | |||
LOGITS_PROCESSOR_INPUTS_DOCSTRING = r""" | |||
Args: | |||
input_ids (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`): | |||
Indices of input sequence tokens in the vocabulary. | |||
Indices can be obtained using :class:`~transformers.BertTokenizer`. See | |||
:meth:`transformers.PreTrainedTokenizer.encode` and :meth:`transformers.PreTrainedTokenizer.__call__` for | |||
details. | |||
`What are input IDs? <../glossary.html#input-ids>`__ | |||
scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, config.vocab_size)`): | |||
Prediction scores of a language modeling head. These can be logits for each vocabulary when not using beam | |||
search or log softmax for each vocabulary token when using beam search | |||
kwargs: | |||
Additional logits processor specific kwargs. | |||
Return: | |||
:obj:`torch.FloatTensor` of shape :obj:`(batch_size, config.vocab_size)`: The processed prediction scores. | |||
""" | |||
class LogitsProcessor(ABC): | |||
"""Abstract base class for all logit processors that can be applied during generation.""" | |||
@add_start_docstrings(LOGITS_PROCESSOR_INPUTS_DOCSTRING) | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
"""Torch method for processing logits.""" | |||
raise NotImplementedError( | |||
f"{self.__class__} is an abstract class. Only classes inheriting this class can be called." | |||
) | |||
class LogitsWarper(ABC): | |||
"""Abstract base class for all logit warpers that can be applied during generation with multinomial sampling.""" | |||
@add_start_docstrings(LOGITS_PROCESSOR_INPUTS_DOCSTRING) | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
"""Torch method for warping logits.""" | |||
raise NotImplementedError( | |||
f"{self.__class__} is an abstract class. Only classes inheriting this class can be called." | |||
) | |||
class LogitsProcessorList(list): | |||
""" | |||
This class can be used to create a list of :class:`~transformers.LogitsProcessor` or | |||
:class:`~transformers.LogitsWarper` to subsequently process a :obj:`scores` input tensor. This class inherits from | |||
list and adds a specific `__call__` method to apply each :class:`~transformers.LogitsProcessor` or | |||
:class:`~transformers.LogitsWarper` to the inputs. | |||
""" | |||
@add_start_docstrings(LOGITS_PROCESSOR_INPUTS_DOCSTRING) | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor", **kwargs) -> "torch.FloatTensor": | |||
for processor in self: | |||
function_args = inspect.signature(processor.__call__).parameters | |||
if len(function_args) > 2: | |||
assert all( | |||
arg in kwargs for arg in list(function_args.keys())[2:] | |||
), f"Make sure that all the required parameters: {list(function_args.keys())} for {processor.__class__} are passed to the logits processor." | |||
scores = processor(input_ids, scores, **kwargs) | |||
else: | |||
scores = processor(input_ids, scores) | |||
return scores | |||
class MinLengthLogitsProcessor(LogitsProcessor): | |||
r""" | |||
:class:`transformers.LogitsProcessor` enforcing a min-length by setting EOS probability to 0. | |||
Args: | |||
min_length (:obj:`int`): | |||
The minimum length below which the score of :obj:`eos_token_id` is set to :obj:`-float("Inf")`. | |||
eos_token_id (:obj:`int`): | |||
The id of the `end-of-sequence` token. | |||
""" | |||
def __init__(self, min_length: int, eos_token_id: int): | |||
if not isinstance(min_length, int) or min_length < 0: | |||
raise ValueError(f"`min_length` has to be a positive integer, but is {min_length}") | |||
if not isinstance(eos_token_id, int) or eos_token_id < 0: | |||
raise ValueError(f"`eos_token_id` has to be a positive integer, but is {eos_token_id}") | |||
self.min_length = min_length | |||
self.eos_token_id = eos_token_id | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
cur_len = input_ids.shape[-1] | |||
if cur_len < self.min_length: | |||
scores[:, self.eos_token_id] = -float("inf") | |||
return scores | |||
class TemperatureLogitsWarper(LogitsWarper): | |||
r""" | |||
:class:`transformers.LogitsWarper` for temperature (exponential scaling output probability distribution). | |||
Args: | |||
temperature (:obj:`float`): | |||
The value used to module the logits distribution. | |||
""" | |||
def __init__(self, temperature: float): | |||
if not isinstance(temperature, float) or not (temperature > 0): | |||
raise ValueError(f"`temperature` has to be a strictly positive float, but is {temperature}") | |||
self.temperature = temperature | |||
def __call__(self, input_ids: "torch.Tensor", scores: "torch.Tensor") -> "torch.FloatTensor": | |||
scores = scores / self.temperature | |||
return scores | |||
class RepetitionPenaltyLogitsProcessor(LogitsProcessor): | |||
r""" | |||
:class:`transformers.LogitsProcessor` enforcing an exponential penalty on repeated sequences. | |||
Args: | |||
repetition_penalty (:obj:`float`): | |||
The parameter for repetition penalty. 1.0 means no penalty. See `this paper | |||
<https://arxiv.org/pdf/1909.05858.pdf>`__ for more details. | |||
""" | |||
def __init__(self, penalty: float): | |||
if not isinstance(penalty, float) or not (penalty > 0): | |||
raise ValueError(f"`penalty` has to be a strictly positive float, but is {penalty}") | |||
self.penalty = penalty | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
score = torch.gather(scores, 1, input_ids) | |||
# if score < 0 then repetition penalty has to be multiplied to reduce the previous token probability | |||
score = torch.where(score < 0, score * self.penalty, score / self.penalty) | |||
scores.scatter_(1, input_ids, score) | |||
return scores | |||
class TopPLogitsWarper(LogitsWarper): | |||
""" | |||
:class:`transformers.LogitsWarper` that performs top-p, i.e. restricting to top tokens summing to prob_cut_off <= | |||
prob_cut_off. | |||
Args: | |||
top_p (:obj:`float`): | |||
If set to < 1, only the most probable tokens with probabilities that add up to :obj:`top_p` or higher are | |||
kept for generation. | |||
filter_value (:obj:`float`, `optional`, defaults to :obj:`-float("Inf")`): | |||
All filtered values will be set to this float value. | |||
min_tokens_to_keep (:obj:`int`, `optional`, defaults to 1): | |||
Minimum number of tokens that cannot be filtered. | |||
""" | |||
def __init__(self, top_p: float, filter_value: float = -float("Inf"), min_tokens_to_keep: int = 1): | |||
top_p = float(top_p) | |||
if top_p < 0 or top_p > 1.0: | |||
raise ValueError(f"`top_p` has to be a float > 0 and < 1, but is {top_p}") | |||
self.top_p = top_p | |||
self.filter_value = filter_value | |||
self.min_tokens_to_keep = min_tokens_to_keep | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
sorted_logits, sorted_indices = torch.sort(scores, descending=True) | |||
cumulative_probs = sorted_logits.softmax(dim=-1).cumsum(dim=-1) | |||
# Remove tokens with cumulative top_p above the threshold (token with 0 are kept) | |||
sorted_indices_to_remove = cumulative_probs > self.top_p | |||
if self.min_tokens_to_keep > 1: | |||
# Keep at least min_tokens_to_keep (set to min_tokens_to_keep-1 because we add the first one below) | |||
sorted_indices_to_remove[..., : self.min_tokens_to_keep - 1] = 0 | |||
# Shift the indices to the right to keep also the first token above the threshold | |||
sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone() | |||
sorted_indices_to_remove[..., 0] = 0 | |||
# scatter sorted tensors to original indexing | |||
indices_to_remove = sorted_indices_to_remove.scatter(1, sorted_indices, sorted_indices_to_remove) | |||
scores = scores.masked_fill(indices_to_remove, self.filter_value) | |||
return scores | |||
class TopKLogitsWarper(LogitsWarper): | |||
r""" | |||
:class:`transformers.LogitsWarper` that performs top-k, i.e. restricting to the k highest probability elements. | |||
Args: | |||
top_k (:obj:`int`): | |||
The number of highest probability vocabulary tokens to keep for top-k-filtering. | |||
filter_value (:obj:`float`, `optional`, defaults to :obj:`-float("Inf")`): | |||
All filtered values will be set to this float value. | |||
min_tokens_to_keep (:obj:`int`, `optional`, defaults to 1): | |||
Minimum number of tokens that cannot be filtered. | |||
""" | |||
def __init__(self, top_k: int, filter_value: float = -float("Inf"), min_tokens_to_keep: int = 1): | |||
if not isinstance(top_k, int) or top_k <= 0: | |||
raise ValueError(f"`top_k` has to be a strictly positive integer, but is {top_k}") | |||
self.top_k = top_k | |||
self.filter_value = filter_value | |||
self.min_tokens_to_keep = min_tokens_to_keep | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
top_k = min(max(self.top_k, self.min_tokens_to_keep), scores.size(-1)) # Safety check | |||
# Remove all tokens with a probability less than the last token of the top-k | |||
indices_to_remove = scores < torch.topk(scores, top_k)[0][..., -1, None] | |||
scores = scores.masked_fill(indices_to_remove, self.filter_value) | |||
return scores | |||
def _get_ngrams(ngram_size: int, prev_input_ids: "torch.Tensor", num_hypos: int): | |||
generated_ngrams = [{} for _ in range(num_hypos)] | |||
for idx in range(num_hypos): | |||
gen_tokens = prev_input_ids[idx].tolist() | |||
generated_ngram = generated_ngrams[idx] | |||
for ngram in zip(*[gen_tokens[i:] for i in range(ngram_size)]): | |||
prev_ngram_tuple = tuple(ngram[:-1]) | |||
generated_ngram[prev_ngram_tuple] = generated_ngram.get(prev_ngram_tuple, []) + [ngram[-1]] | |||
return generated_ngrams | |||
def _get_generated_ngrams(banned_ngrams, prev_input_ids, ngram_size, cur_len): | |||
# Before decoding the next token, prevent decoding of ngrams that have already appeared | |||
start_idx = cur_len + 1 - ngram_size | |||
ngram_idx = tuple(prev_input_ids[start_idx:cur_len].tolist()) | |||
return banned_ngrams.get(ngram_idx, []) | |||
def _calc_banned_ngram_tokens( | |||
ngram_size: int, prev_input_ids: "torch.Tensor", num_hypos: int, cur_len: int | |||
) -> List[Iterable[int]]: | |||
"""Copied from fairseq for no_repeat_ngram in beam_search""" | |||
if cur_len + 1 < ngram_size: | |||
# return no banned tokens if we haven't generated no_repeat_ngram_size tokens yet | |||
return [[] for _ in range(num_hypos)] | |||
generated_ngrams = _get_ngrams(ngram_size, prev_input_ids, num_hypos) | |||
banned_tokens = [ | |||
_get_generated_ngrams(generated_ngrams[hypo_idx], prev_input_ids[hypo_idx], ngram_size, cur_len) | |||
for hypo_idx in range(num_hypos) | |||
] | |||
return banned_tokens | |||
class NoRepeatNGramLogitsProcessor(LogitsProcessor): | |||
r""" | |||
:class:`transformers.LogitsProcessor` that enforces no repetition of n-grams. See `Fairseq | |||
<https://github.com/pytorch/fairseq/blob/a07cb6f40480928c9e0548b737aadd36ee66ac76/fairseq/sequence_generator.py#L345>`__. | |||
Args: | |||
ngram_size (:obj:`int`): | |||
All ngrams of size :obj:`ngram_size` can only occur once. | |||
""" | |||
def __init__(self, ngram_size: int): | |||
if not isinstance(ngram_size, int) or ngram_size <= 0: | |||
raise ValueError(f"`ngram_size` has to be a strictly positive integer, but is {ngram_size}") | |||
self.ngram_size = ngram_size | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
num_batch_hypotheses = scores.shape[0] | |||
cur_len = input_ids.shape[-1] | |||
banned_batch_tokens = _calc_banned_ngram_tokens(self.ngram_size, input_ids, num_batch_hypotheses, cur_len) | |||
for i, banned_tokens in enumerate(banned_batch_tokens): | |||
scores[i, banned_tokens] = -float("inf") | |||
return scores | |||
class EncoderNoRepeatNGramLogitsProcessor(LogitsProcessor): | |||
r""" | |||
:class:`transformers.LogitsProcessor` that enforces no repetition of encoder input ids n-grams for the decoder ids. | |||
See `ParlAI <https://github.com/facebookresearch/ParlAI/blob/master/parlai/core/torch_generator_agent.py#L1350>`__. | |||
Args: | |||
encoder_ngram_size (:obj:`int`): | |||
All ngrams of size :obj:`ngram_size` can only occur within the encoder input ids. | |||
encoder_input_ids (:obj:`int`): | |||
The encoder_input_ids that should not be repeated within the decoder ids. | |||
""" | |||
def __init__(self, encoder_ngram_size: int, encoder_input_ids: "torch.LongTensor"): | |||
if not isinstance(encoder_ngram_size, int) or encoder_ngram_size <= 0: | |||
raise ValueError( | |||
f"`encoder_ngram_size` has to be a strictly positive integer, but is {encoder_ngram_size}" | |||
) | |||
self.ngram_size = encoder_ngram_size | |||
if len(encoder_input_ids.shape) == 1: | |||
encoder_input_ids = encoder_input_ids.unsqueeze(0) | |||
self.batch_size = encoder_input_ids.shape[0] | |||
self.generated_ngrams = _get_ngrams(encoder_ngram_size, encoder_input_ids, self.batch_size) | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
# B x num_beams | |||
num_hypos = scores.shape[0] | |||
num_beams = num_hypos // self.batch_size | |||
cur_len = input_ids.shape[-1] | |||
banned_batch_tokens = [ | |||
_get_generated_ngrams( | |||
self.generated_ngrams[hypo_idx // num_beams], input_ids[hypo_idx], self.ngram_size, cur_len | |||
) | |||
for hypo_idx in range(num_hypos) | |||
] | |||
for i, banned_tokens in enumerate(banned_batch_tokens): | |||
scores[i, banned_tokens] = -float("inf") | |||
return scores | |||
class NoBadWordsLogitsProcessor(LogitsProcessor): | |||
""" | |||
:class:`transformers.LogitsProcessor` that enforces that specified sequences will never be sampled. | |||
Args: | |||
bad_words_ids (:obj:`List[List[int]]`): | |||
List of list of token ids that are not allowed to be generated. In order to get the tokens of the words | |||
that should not appear in the generated text, use :obj:`tokenizer(bad_word, | |||
add_prefix_space=True).input_ids`. | |||
eos_token_id (:obj:`int`): | |||
The id of the `end-of-sequence` token. | |||
""" | |||
def __init__(self, bad_words_ids: List[List[int]], eos_token_id: int): | |||
if not isinstance(bad_words_ids, List) or len(bad_words_ids) == 0: | |||
raise ValueError(f"`bad_words_ids` has to be a non-emtpy list, but is {bad_words_ids}.") | |||
if any(not isinstance(bad_word_ids, list) for bad_word_ids in bad_words_ids): | |||
raise ValueError(f"`bad_words_ids` has to be a list of lists, but is {bad_words_ids}.") | |||
if any( | |||
any((not isinstance(token_id, (int, np.integer)) or token_id < 0) for token_id in bad_word_ids) | |||
for bad_word_ids in bad_words_ids | |||
): | |||
raise ValueError( | |||
f"Each list in `bad_words_ids` has to be a list of positive integers, but is {bad_words_ids}." | |||
) | |||
bad_words_ids = list(filter(lambda bad_token_seq: bad_token_seq != [eos_token_id], bad_words_ids)) | |||
self.bad_words_id_length_1 = [] | |||
self.bad_words_id_length_greater_than_1 = [] | |||
for word in bad_words_ids: | |||
if len(word) == 1: | |||
self.bad_words_id_length_1.append(word[0]) | |||
else: | |||
self.bad_words_id_length_greater_than_1.append(word) | |||
self.static_bad_words_mask: Optional[torch.LongTensor] = None | |||
for banned_token_seq in self.bad_words_id_length_greater_than_1: | |||
assert len(banned_token_seq) > 0, f"Banned words token sequences {bad_words_ids} cannot have an empty list" | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
if self.static_bad_words_mask is None and len(self.bad_words_id_length_1) > 0: | |||
self.static_bad_words_mask = self._calc_static_bad_word_mask(scores) | |||
dynamic_banned_tokens = self._calc_banned_bad_words_ids(input_ids.tolist()) | |||
scores = self._set_scores_to_inf_for_banned_tokens(scores, dynamic_banned_tokens) | |||
return scores | |||
def _calc_static_bad_word_mask(self, scores: "torch.FloatTensor") -> "torch.BoolTensor": | |||
static_bad_words_mask = torch.zeros(scores.shape[1]) | |||
static_bad_words_mask[self.bad_words_id_length_1] = 1 | |||
return static_bad_words_mask.unsqueeze(0).to(scores.device).bool() | |||
def _tokens_match(self, prev_tokens: List[int], tokens: List[int]) -> bool: | |||
if len(tokens) == 0: | |||
# if bad word tokens is just one token always ban it | |||
return True | |||
elif len(tokens) > len(prev_tokens): | |||
# if bad word tokens are longer then prev input_ids they can't be equal | |||
return False | |||
else: | |||
return prev_tokens[-len(tokens) :] == tokens | |||
def _calc_banned_bad_words_ids(self, prev_input_ids: List[List[int]]) -> Iterable[int]: | |||
banned_tokens = [] | |||
for prev_input_ids_slice in prev_input_ids: | |||
banned_tokens_slice = [] | |||
for banned_token_seq in self.bad_words_id_length_greater_than_1: | |||
if self._tokens_match(prev_input_ids_slice, banned_token_seq[:-1]): | |||
banned_tokens_slice.append(banned_token_seq[-1]) | |||
banned_tokens.append(banned_tokens_slice) | |||
return banned_tokens | |||
def _set_scores_to_inf_for_banned_tokens( | |||
self, scores: "torch.Tensor", banned_tokens: List[List[int]] | |||
) -> "torch.Tensor": | |||
""" | |||
Modifies the scores in place by setting the banned token positions to `-inf`. Banned token is expected to be a | |||
list of list of banned tokens to ban in the format [[batch index, vocabulary position],... | |||
Args: | |||
scores: logits distribution of shape (batch size, vocabulary size) | |||
banned_tokens: list of list of tokens to ban of length (batch_size) | |||
""" | |||
banned_mask_list = [] | |||
for idx, batch_banned_tokens in enumerate(banned_tokens): | |||
for token in batch_banned_tokens: | |||
# Eliminates invalid bad word IDs that are over the vocabulary size. | |||
if token <= scores.shape[1]: | |||
banned_mask_list.append([idx, token]) | |||
else: | |||
logger.error( | |||
f"An invalid bad word ID is defined: {token}. This ID is not contained in the" | |||
f"vocabulary, and is therefore ignored." | |||
) | |||
if not banned_mask_list and self.static_bad_words_mask is None: | |||
return scores | |||
else: | |||
if banned_mask_list: | |||
banned_mask = torch.LongTensor(banned_mask_list) | |||
indices = torch.ones(len(banned_mask)) | |||
# A sparse tensor is generated from a list of coordinates: [[0, 1], [0, 2], [2, 0]]. A conversion to dense tensor generates: | |||
# [ 0 1 1 ] | |||
# [ 0 0 0 ] | |||
# [ 1 0 0 ] | |||
banned_mask = ( | |||
torch.sparse.LongTensor(banned_mask.t(), indices, scores.size()) | |||
.to(scores.device) | |||
.to_dense() | |||
.bool() | |||
) | |||
if self.static_bad_words_mask is not None: | |||
banned_mask = torch.bitwise_or(banned_mask, self.static_bad_words_mask) | |||
else: | |||
banned_mask = self.static_bad_words_mask | |||
scores = scores.masked_fill(banned_mask, -float("inf")) | |||
return scores | |||
class PrefixConstrainedLogitsProcessor(LogitsProcessor): | |||
r""" | |||
:class:`transformers.LogitsProcessor` that enforces constrained generation and is useful for prefix-conditioned | |||
constrained generation. See `Autoregressive Entity Retrieval <https://arxiv.org/abs/2010.00904>`__ for more | |||
information. | |||
Args: | |||
prefix_allowed_tokens_fn: (:obj:`Callable[[int, torch.Tensor], List[int]]`): | |||
This function constraints the beam search to allowed tokens only at each step. This function takes 2 | |||
arguments :obj:`inputs_ids` and the batch ID :obj:`batch_id`. It has to return a list with the allowed | |||
tokens for the next generation step conditioned on the previously generated tokens :obj:`inputs_ids` and | |||
the batch ID :obj:`batch_id`. | |||
""" | |||
def __init__(self, prefix_allowed_tokens_fn: Callable[[int, "torch.Tensor"], List[int]], num_beams: int): | |||
self._prefix_allowed_tokens_fn = prefix_allowed_tokens_fn | |||
self._num_beams = num_beams | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
mask = torch.full_like(scores, -math.inf) | |||
for batch_id, beam_sent in enumerate(input_ids.view(-1, self._num_beams, input_ids.shape[-1])): | |||
for beam_id, sent in enumerate(beam_sent): | |||
mask[batch_id * self._num_beams + beam_id, self._prefix_allowed_tokens_fn(batch_id, sent)] = 0 | |||
return scores + mask | |||
class HammingDiversityLogitsProcessor(LogitsProcessor): | |||
r""" | |||
:class:`transformers.LogitsProcessor` that enforces diverse beam search. Note that this logits processor is only | |||
effective for :meth:`transformers.PreTrainedModel.group_beam_search`. See `Diverse Beam Search: Decoding Diverse | |||
Solutions from Neural Sequence Models <https://arxiv.org/pdf/1610.02424.pdf>`__ for more details. | |||
Args: | |||
diversity_penalty (:obj:`float`): | |||
This value is subtracted from a beam's score if it generates a token same as any beam from other group at a | |||
particular time. Note that :obj:`diversity_penalty` is only effective if ``group beam search`` is enabled. | |||
num_beams (:obj:`int`): | |||
Number of beams used for group beam search. See `this paper <https://arxiv.org/pdf/1610.02424.pdf>`__ for | |||
more details. | |||
num_beam_groups (:obj:`int`): | |||
Number of groups to divide :obj:`num_beams` into in order to ensure diversity among different groups of | |||
beams. See `this paper <https://arxiv.org/pdf/1610.02424.pdf>`__ for more details. | |||
""" | |||
def __init__(self, diversity_penalty: float, num_beams: int, num_beam_groups: int): | |||
if not isinstance(diversity_penalty, float) or (not diversity_penalty > 0.0): | |||
raise ValueError("`diversity_penalty` should be a float strictly larger than 0.") | |||
self._diversity_penalty = diversity_penalty | |||
if not isinstance(num_beams, int) or num_beams < 2: | |||
raise ValueError("`num_beams` should be an integer strictly larger than 1.") | |||
self._num_beams = num_beams | |||
if not isinstance(num_beam_groups, int) or num_beam_groups < 2: | |||
raise ValueError("`num_beam_groups` should be an integer strictly larger than 1.") | |||
if num_beam_groups > num_beams: | |||
raise ValueError("`beam_groups` has to be smaller or equal to `num_beams`.") | |||
self._num_sub_beams = num_beams // num_beam_groups | |||
def __call__( | |||
self, | |||
input_ids: "torch.LongTensor", | |||
scores: "torch.FloatTensor", | |||
current_tokens: "torch.LongTensor", | |||
beam_group_idx: int, | |||
) -> "torch.FloatTensor": | |||
# hamming diversity: penalise using same token in current group which was used in previous groups at | |||
# the same time step | |||
batch_size = current_tokens.shape[0] // self._num_beams | |||
group_start_idx = beam_group_idx * self._num_sub_beams | |||
group_end_idx = min(group_start_idx + self._num_sub_beams, self._num_beams) | |||
group_size = group_end_idx - group_start_idx | |||
vocab_size = scores.shape[-1] | |||
if group_start_idx == 0: | |||
return scores | |||
for batch_idx in range(batch_size): | |||
# predicted tokens of last time step of previous groups | |||
previous_group_tokens = current_tokens[ | |||
batch_idx * self._num_beams : batch_idx * self._num_beams + group_start_idx | |||
] | |||
token_frequency = torch.bincount(previous_group_tokens, minlength=vocab_size).to(scores.device) | |||
scores[batch_idx * group_size : (batch_idx + 1) * group_size] -= self._diversity_penalty * token_frequency | |||
return scores | |||
class ForcedBOSTokenLogitsProcessor(LogitsProcessor): | |||
r""" | |||
:class:`~transformers.LogitsProcessor` that enforces the specified token as the first generated token. | |||
Args: | |||
bos_token_id (:obj:`int`): | |||
The id of the token to force as the first generated token. | |||
""" | |||
def __init__(self, bos_token_id: int): | |||
self.bos_token_id = bos_token_id | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
cur_len = input_ids.shape[-1] | |||
if cur_len == 1: | |||
num_tokens = scores.shape[1] | |||
scores[:, [i for i in range(num_tokens) if i != self.bos_token_id]] = -float("inf") | |||
scores[:, self.bos_token_id] = 0 | |||
return scores | |||
class ForcedEOSTokenLogitsProcessor(LogitsProcessor): | |||
r""" | |||
:class:`~transformers.LogitsProcessor` that enforces the specified token as the last generated token when | |||
:obj:`max_length` is reached. | |||
Args: | |||
max_length (:obj:`int`): | |||
The maximum length of the sequence to be generated. | |||
eos_token_id (:obj:`int`): | |||
The id of the token to force as the last generated token when :obj:`max_length` is reached. | |||
""" | |||
def __init__(self, max_length: int, eos_token_id: int): | |||
self.max_length = max_length | |||
self.eos_token_id = eos_token_id | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
cur_len = input_ids.shape[-1] | |||
if cur_len == self.max_length - 1: | |||
num_tokens = scores.shape[1] | |||
scores[:, [i for i in range(num_tokens) if i != self.eos_token_id]] = -float("inf") | |||
scores[:, self.eos_token_id] = 0 | |||
return scores | |||
class InfNanRemoveLogitsProcessor(LogitsProcessor): | |||
r""" | |||
:class:`~transformers.LogitsProcessor` that removes all :obj:`nan` and :obj:`inf` values to avoid the generation | |||
method to fail. Note that using the logits processor should only be used if necessary since it can slow down the | |||
generation method. :obj:`max_length` is reached. | |||
""" | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor") -> "torch.FloatTensor": | |||
# set all nan values to 0.0 | |||
scores[scores != scores] = 0.0 | |||
# set all inf values to max possible value | |||
scores[scores == float("inf")] = torch.finfo(scores.dtype).max | |||
return scores |
@@ -0,0 +1,128 @@ | |||
import time | |||
from abc import ABC | |||
from copy import deepcopy | |||
from typing import Optional | |||
from .file_utils import add_start_docstrings | |||
from fastNLP.envs.imports import _NEED_IMPORT_TORCH | |||
from fastNLP.core.log import logger | |||
if _NEED_IMPORT_TORCH: | |||
import torch | |||
STOPPING_CRITERIA_INPUTS_DOCSTRING = r""" | |||
Args: | |||
input_ids (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`): | |||
Indices of input sequence tokens in the vocabulary. | |||
Indices can be obtained using :class:`~transformers.BertTokenizer`. See | |||
:meth:`transformers.PreTrainedTokenizer.encode` and :meth:`transformers.PreTrainedTokenizer.__call__` for | |||
details. | |||
`What are input IDs? <../glossary.html#input-ids>`__ | |||
scores (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, config.vocab_size)`): | |||
Prediction scores of a language modeling head. These can be scores for each vocabulary token before SoftMax | |||
or scores for each vocabulary token after SoftMax. | |||
kwargs: | |||
Additional stopping criteria specific kwargs. | |||
Return: | |||
:obj:`bool`. :obj:`False` indicates we should continue, :obj:`True` indicates we should stop. | |||
""" | |||
class StoppingCriteria(ABC): | |||
"""Abstract base class for all stopping criteria that can be applied during generation.""" | |||
@add_start_docstrings(STOPPING_CRITERIA_INPUTS_DOCSTRING) | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor", **kwargs) -> bool: | |||
raise NotImplementedError("StoppingCriteria needs to be subclassed") | |||
class MaxLengthCriteria(StoppingCriteria): | |||
""" | |||
This class can be used to stop generation whenever the full generated number of tokens exceeds :obj:`max_length`. | |||
Keep in mind for decoder-only type of transformers, this will include the initial prompted tokens. | |||
Args: | |||
max_length (:obj:`int`): | |||
The maximum length that the output sequence can have in number of tokens. | |||
""" | |||
def __init__(self, max_length: int): | |||
self.max_length = max_length | |||
@add_start_docstrings(STOPPING_CRITERIA_INPUTS_DOCSTRING) | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor", **kwargs) -> bool: | |||
return input_ids.shape[-1] >= self.max_length | |||
class MaxNewTokensCriteria(StoppingCriteria): | |||
""" | |||
This class can be used to stop generation whenever the generated number of tokens exceeds :obj:`max_new_tokens`. | |||
Keep in mind for decoder-only type of transformers, this will **not** include the initial prompted tokens. This is | |||
very close to :obj:`MaxLengthCriteria` but ignores the number of initial tokens. | |||
Args: | |||
start_length (:obj:`int`): | |||
The number of initial tokens. | |||
max_new_tokens (:obj:`int`): | |||
The maximum number of tokens to generate. | |||
""" | |||
def __init__(self, start_length: int, max_new_tokens: int): | |||
self.start_length = start_length | |||
self.max_new_tokens = max_new_tokens | |||
self.max_length = start_length + max_new_tokens | |||
@add_start_docstrings(STOPPING_CRITERIA_INPUTS_DOCSTRING) | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor", **kwargs) -> bool: | |||
return input_ids.shape[-1] >= self.max_length | |||
class MaxTimeCriteria(StoppingCriteria): | |||
""" | |||
This class can be used to stop generation whenever the full generation exceeds some amount of time. By default, the | |||
time will start being counted when you initialize this function. You can override this by passing an | |||
:obj:`initial_time`. | |||
Args: | |||
max_time (:obj:`float`): | |||
The maximum allowed time in seconds for the generation. | |||
initial_time (:obj:`float`, `optional`, defaults to :obj:`time.time()`): | |||
The start of the generation allowed time. | |||
""" | |||
def __init__(self, max_time: float, initial_timestamp: Optional[float] = None): | |||
self.max_time = max_time | |||
self.initial_timestamp = time.time() if initial_timestamp is None else initial_timestamp | |||
@add_start_docstrings(STOPPING_CRITERIA_INPUTS_DOCSTRING) | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor", **kwargs) -> bool: | |||
return time.time() - self.initial_timestamp > self.max_time | |||
class StoppingCriteriaList(list): | |||
@add_start_docstrings(STOPPING_CRITERIA_INPUTS_DOCSTRING) | |||
def __call__(self, input_ids: "torch.LongTensor", scores: "torch.FloatTensor", **kwargs) -> bool: | |||
return any(criteria(input_ids, scores) for criteria in self) | |||
@property | |||
def max_length(self) -> Optional[int]: | |||
for stopping_criterium in self: | |||
if isinstance(stopping_criterium, MaxLengthCriteria): | |||
return stopping_criterium.max_length | |||
elif isinstance(stopping_criterium, MaxNewTokensCriteria): | |||
return stopping_criterium.max_length | |||
return None | |||
def validate_stopping_criteria(stopping_criteria: StoppingCriteriaList, max_length: int) -> StoppingCriteriaList: | |||
stopping_max_length = stopping_criteria.max_length | |||
new_stopping_criteria = deepcopy(stopping_criteria) | |||
if stopping_max_length is not None and stopping_max_length != max_length: | |||
logger.warn("You set different `max_length` for stopping criteria and `max_length` parameter", UserWarning) | |||
elif stopping_max_length is None: | |||
new_stopping_criteria.append(MaxLengthCriteria(max_length=max_length)) | |||
return new_stopping_criteria |
@@ -0,0 +1,816 @@ | |||
# Copyright 2020 The HuggingFace Team. 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. | |||
from dataclasses import dataclass | |||
from typing import Optional, Tuple | |||
from .file_utils import ModelOutput | |||
from fastNLP.envs.imports import _NEED_IMPORT_TORCH | |||
if _NEED_IMPORT_TORCH: | |||
import torch | |||
@dataclass | |||
class BaseModelOutput(ModelOutput): | |||
""" | |||
Base class for model's outputs, with potential hidden states and attentions. | |||
Args: | |||
last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`): | |||
Sequence of hidden-states at the output of the last layer of the model. | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
last_hidden_state: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class BaseModelOutputWithPooling(ModelOutput): | |||
""" | |||
Base class for model's outputs that also contains a pooling of the last hidden states. | |||
Args: | |||
last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`): | |||
Sequence of hidden-states at the output of the last layer of the model. | |||
pooler_output (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, hidden_size)`): | |||
Last layer hidden-state of the first token of the sequence (classification token) after further processing | |||
through the layers used for the auxiliary pretraining task. E.g. for BERT-family of models, this returns | |||
the classification token after processing through a linear layer and a tanh activation function. The linear | |||
layer weights are trained from the next sentence prediction (classification) objective during pretraining. | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
last_hidden_state: "torch.FloatTensor" = None | |||
pooler_output: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class BaseModelOutputWithPast(ModelOutput): | |||
""" | |||
Base class for model's outputs that may also contain a past key/values (to speed up sequential decoding). | |||
Args: | |||
last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`): | |||
Sequence of hidden-states at the output of the last layer of the model. | |||
If :obj:`past_key_values` is used only the last hidden-state of the sequences of shape :obj:`(batch_size, | |||
1, hidden_size)` is output. | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`tuple(torch.FloatTensor)` of length :obj:`config.n_layers`, with each tuple having 2 tensors | |||
of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and optionally if | |||
``config.is_encoder_decoder=True`` 2 additional tensors of shape :obj:`(batch_size, num_heads, | |||
encoder_sequence_length, embed_size_per_head)`. | |||
Contains pre-computed hidden-states (key and values in the self-attention blocks and optionally if | |||
``config.is_encoder_decoder=True`` in the cross-attention blocks) that can be used (see | |||
:obj:`past_key_values` input) to speed up sequential decoding. | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
last_hidden_state: "torch.FloatTensor" = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class BaseModelOutputWithCrossAttentions(ModelOutput): | |||
""" | |||
Base class for model's outputs, with potential hidden states and attentions. | |||
Args: | |||
last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`): | |||
Sequence of hidden-states at the output of the last layer of the model. | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
cross_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` and ``config.add_cross_attention=True`` is passed or 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 of the decoder's cross-attention layer, after the attention softmax, used to compute the | |||
weighted average in the cross-attention heads. | |||
""" | |||
last_hidden_state: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
cross_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class BaseModelOutputWithPoolingAndCrossAttentions(ModelOutput): | |||
""" | |||
Base class for model's outputs that also contains a pooling of the last hidden states. | |||
Args: | |||
last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`): | |||
Sequence of hidden-states at the output of the last layer of the model. | |||
pooler_output (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, hidden_size)`): | |||
Last layer hidden-state of the first token of the sequence (classification token) after further processing | |||
through the layers used for the auxiliary pretraining task. E.g. for BERT-family of models, this returns | |||
the classification token after processing through a linear layer and a tanh activation function. The linear | |||
layer weights are trained from the next sentence prediction (classification) objective during pretraining. | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
cross_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` and ``config.add_cross_attention=True`` is passed or 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 of the decoder's cross-attention layer, after the attention softmax, used to compute the | |||
weighted average in the cross-attention heads. | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`tuple(torch.FloatTensor)` of length :obj:`config.n_layers`, with each tuple having 2 tensors | |||
of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and optionally if | |||
``config.is_encoder_decoder=True`` 2 additional tensors of shape :obj:`(batch_size, num_heads, | |||
encoder_sequence_length, embed_size_per_head)`. | |||
Contains pre-computed hidden-states (key and values in the self-attention blocks and optionally if | |||
``config.is_encoder_decoder=True`` in the cross-attention blocks) that can be used (see | |||
:obj:`past_key_values` input) to speed up sequential decoding. | |||
""" | |||
last_hidden_state: "torch.FloatTensor" = None | |||
pooler_output: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
cross_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class BaseModelOutputWithPastAndCrossAttentions(ModelOutput): | |||
""" | |||
Base class for model's outputs that may also contain a past key/values (to speed up sequential decoding). | |||
Args: | |||
last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`): | |||
Sequence of hidden-states at the output of the last layer of the model. | |||
If :obj:`past_key_values` is used only the last hidden-state of the sequences of shape :obj:`(batch_size, | |||
1, hidden_size)` is output. | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`tuple(torch.FloatTensor)` of length :obj:`config.n_layers`, with each tuple having 2 tensors | |||
of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and optionally if | |||
``config.is_encoder_decoder=True`` 2 additional tensors of shape :obj:`(batch_size, num_heads, | |||
encoder_sequence_length, embed_size_per_head)`. | |||
Contains pre-computed hidden-states (key and values in the self-attention blocks and optionally if | |||
``config.is_encoder_decoder=True`` in the cross-attention blocks) that can be used (see | |||
:obj:`past_key_values` input) to speed up sequential decoding. | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
cross_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` and ``config.add_cross_attention=True`` is passed or 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 of the decoder's cross-attention layer, after the attention softmax, used to compute the | |||
weighted average in the cross-attention heads. | |||
""" | |||
last_hidden_state: "torch.FloatTensor" = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
cross_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class Seq2SeqModelOutput(ModelOutput): | |||
""" | |||
Base class for model encoder's outputs that also contains : pre-computed hidden states that can speed up sequential | |||
decoding. | |||
Args: | |||
last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`): | |||
Sequence of hidden-states at the output of the last layer of the decoder of the model. | |||
If :obj:`past_key_values` is used only the last hidden-state of the sequences of shape :obj:`(batch_size, | |||
1, hidden_size)` is output. | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`tuple(torch.FloatTensor)` of length :obj:`config.n_layers`, with each tuple having 2 tensors | |||
of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and 2 additional tensors of | |||
shape :obj:`(batch_size, num_heads, encoder_sequence_length, embed_size_per_head)`. | |||
Contains pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention | |||
blocks) that can be used (see :obj:`past_key_values` input) to speed up sequential decoding. | |||
decoder_hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 decoder at the output of each layer plus the initial embedding outputs. | |||
decoder_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the decoder, after the attention softmax, used to compute the weighted average in the | |||
self-attention heads. | |||
cross_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the decoder's cross-attention layer, after the attention softmax, used to compute the | |||
weighted average in the cross-attention heads. | |||
encoder_last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): | |||
Sequence of hidden-states at the output of the last layer of the encoder of the model. | |||
encoder_hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 encoder at the output of each layer plus the initial embedding outputs. | |||
encoder_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the encoder, after the attention softmax, used to compute the weighted average in the | |||
self-attention heads. | |||
""" | |||
last_hidden_state: "torch.FloatTensor" = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
decoder_hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
decoder_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
cross_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
encoder_last_hidden_state: Optional["torch.FloatTensor"] = None | |||
encoder_hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
encoder_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class CausalLMOutput(ModelOutput): | |||
""" | |||
Base class for causal language model (or autoregressive) outputs. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided): | |||
Language modeling loss (for next-token prediction). | |||
logits (: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 ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class CausalLMOutputWithPast(ModelOutput): | |||
""" | |||
Base class for causal language model (or autoregressive) outputs. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided): | |||
Language modeling loss (for next-token prediction). | |||
logits (: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). | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`tuple(torch.FloatTensor)` of length :obj:`config.n_layers`, with each tuple having 2 tensors | |||
of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`) | |||
Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see | |||
:obj:`past_key_values` input) to speed up sequential decoding. | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class CausalLMOutputWithCrossAttentions(ModelOutput): | |||
""" | |||
Base class for causal language model (or autoregressive) outputs. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided): | |||
Language modeling loss (for next-token prediction). | |||
logits (: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 ``output_hidden_states=True`` is passed or 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 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. | |||
cross_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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)`. | |||
Cross attentions weights after the attention softmax, used to compute the weighted average in the | |||
cross-attention heads. | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`torch.FloatTensor` tuples of length :obj:`config.n_layers`, with each tuple containing the | |||
cached key, value states of the self-attention and the cross-attention layers if model is used in | |||
encoder-decoder setting. Only relevant if ``config.is_decoder = True``. | |||
Contains pre-computed hidden-states (key and values in the attention blocks) that can be used (see | |||
:obj:`past_key_values` input) to speed up sequential decoding. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
cross_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class SequenceClassifierOutputWithPast(ModelOutput): | |||
""" | |||
Base class for outputs of sentence classification models. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided): | |||
Classification (or regression if config.num_labels==1) loss. | |||
logits (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, config.num_labels)`): | |||
Classification (or regression if config.num_labels==1) scores (before SoftMax). | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`tuple(torch.FloatTensor)` of length :obj:`config.n_layers`, with each tuple having 2 tensors | |||
of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`) | |||
Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see | |||
:obj:`past_key_values` input) to speed up sequential decoding. | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class MaskedLMOutput(ModelOutput): | |||
""" | |||
Base class for masked language models outputs. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided): | |||
Masked language modeling (MLM) loss. | |||
logits (: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 ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class Seq2SeqLMOutput(ModelOutput): | |||
""" | |||
Base class for sequence-to-sequence language models outputs. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided): | |||
Language modeling loss. | |||
logits (: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). | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`tuple(torch.FloatTensor)` of length :obj:`config.n_layers`, with each tuple having 2 tensors | |||
of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and 2 additional tensors of | |||
shape :obj:`(batch_size, num_heads, encoder_sequence_length, embed_size_per_head)`. | |||
Contains pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention | |||
blocks) that can be used (see :obj:`past_key_values` input) to speed up sequential decoding. | |||
decoder_hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 decoder at the output of each layer plus the initial embedding outputs. | |||
decoder_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the decoder, after the attention softmax, used to compute the weighted average in the | |||
self-attention heads. | |||
cross_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the decoder's cross-attention layer, after the attention softmax, used to compute the | |||
weighted average in the cross-attention heads. | |||
encoder_last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): | |||
Sequence of hidden-states at the output of the last layer of the encoder of the model. | |||
encoder_hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 encoder at the output of each layer plus the initial embedding outputs. | |||
encoder_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the encoder, after the attention softmax, used to compute the weighted average in the | |||
self-attention heads. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
decoder_hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
decoder_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
cross_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
encoder_last_hidden_state: Optional["torch.FloatTensor"] = None | |||
encoder_hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
encoder_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class NextSentencePredictorOutput(ModelOutput): | |||
""" | |||
Base class for outputs of models predicting if two sentences are consecutive or not. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`next_sentence_label` is provided): | |||
Next sequence prediction (classification) loss. | |||
logits (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, 2)`): | |||
Prediction scores of the next sequence prediction (classification) head (scores of True/False continuation | |||
before SoftMax). | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class SequenceClassifierOutput(ModelOutput): | |||
""" | |||
Base class for outputs of sentence classification models. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`labels` is provided): | |||
Classification (or regression if config.num_labels==1) loss. | |||
logits (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, config.num_labels)`): | |||
Classification (or regression if config.num_labels==1) scores (before SoftMax). | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class Seq2SeqSequenceClassifierOutput(ModelOutput): | |||
""" | |||
Base class for outputs of sequence-to-sequence sentence classification models. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when :obj:`label` is provided): | |||
Classification (or regression if config.num_labels==1) loss. | |||
logits (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, config.num_labels)`): | |||
Classification (or regression if config.num_labels==1) scores (before SoftMax). | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`tuple(torch.FloatTensor)` of length :obj:`config.n_layers`, with each tuple having 2 tensors | |||
of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and 2 additional tensors of | |||
shape :obj:`(batch_size, num_heads, encoder_sequence_length, embed_size_per_head)`. | |||
Contains pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention | |||
blocks) that can be used (see :obj:`past_key_values` input) to speed up sequential decoding. | |||
decoder_hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 decoder at the output of each layer plus the initial embedding outputs. | |||
decoder_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the decoder, after the attention softmax, used to compute the weighted average in the | |||
self-attention heads. | |||
cross_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the decoder's cross-attention layer, after the attention softmax, used to compute the | |||
weighted average in the cross-attention heads. | |||
encoder_last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): | |||
Sequence of hidden-states at the output of the last layer of the encoder of the model. | |||
encoder_hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 encoder at the output of each layer plus the initial embedding outputs. | |||
encoder_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the encoder, after the attention softmax, used to compute the weighted average in the | |||
self-attention heads. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
decoder_hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
decoder_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
cross_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
encoder_last_hidden_state: Optional["torch.FloatTensor"] = None | |||
encoder_hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
encoder_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class MultipleChoiceModelOutput(ModelOutput): | |||
""" | |||
Base class for outputs of multiple choice models. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape `(1,)`, `optional`, returned when :obj:`labels` is provided): | |||
Classification loss. | |||
logits (: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 ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class TokenClassifierOutput(ModelOutput): | |||
""" | |||
Base class for outputs of token classification models. | |||
Args: | |||
loss (:obj:`torch.FloatTensor` of shape :obj:`(1,)`, `optional`, returned when ``labels`` is provided) : | |||
Classification loss. | |||
logits (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, config.num_labels)`): | |||
Classification scores (before SoftMax). | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
logits: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class QuestionAnsweringModelOutput(ModelOutput): | |||
""" | |||
Base class for outputs of question answering models. | |||
Args: | |||
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_logits (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`): | |||
Span-start scores (before SoftMax). | |||
end_logits (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`): | |||
Span-end scores (before SoftMax). | |||
hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 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. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
start_logits: "torch.FloatTensor" = None | |||
end_logits: "torch.FloatTensor" = None | |||
hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
@dataclass | |||
class Seq2SeqQuestionAnsweringModelOutput(ModelOutput): | |||
""" | |||
Base class for outputs of sequence-to-sequence question answering models. | |||
Args: | |||
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_logits (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`): | |||
Span-start scores (before SoftMax). | |||
end_logits (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`): | |||
Span-end scores (before SoftMax). | |||
past_key_values (:obj:`tuple(tuple(torch.FloatTensor))`, `optional`, returned when ``use_cache=True`` is passed or when ``config.use_cache=True``): | |||
Tuple of :obj:`tuple(torch.FloatTensor)` of length :obj:`config.n_layers`, with each tuple having 2 tensors | |||
of shape :obj:`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and 2 additional tensors of | |||
shape :obj:`(batch_size, num_heads, encoder_sequence_length, embed_size_per_head)`. | |||
Contains pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention | |||
blocks) that can be used (see :obj:`past_key_values` input) to speed up sequential decoding. | |||
decoder_hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 decoder at the output of each layer plus the initial embedding outputs. | |||
decoder_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the decoder, after the attention softmax, used to compute the weighted average in the | |||
self-attention heads. | |||
cross_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the decoder's cross-attention layer, after the attention softmax, used to compute the | |||
weighted average in the cross-attention heads. | |||
encoder_last_hidden_state (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): | |||
Sequence of hidden-states at the output of the last layer of the encoder of the model. | |||
encoder_hidden_states (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_hidden_states=True`` is passed or 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 encoder at the output of each layer plus the initial embedding outputs. | |||
encoder_attentions (:obj:`tuple(torch.FloatTensor)`, `optional`, returned when ``output_attentions=True`` is passed or 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 of the encoder, after the attention softmax, used to compute the weighted average in the | |||
self-attention heads. | |||
""" | |||
loss: Optional["torch.FloatTensor"] = None | |||
start_logits: "torch.FloatTensor" = None | |||
end_logits: "torch.FloatTensor" = None | |||
past_key_values: Optional[Tuple[Tuple["torch.FloatTensor"]]] = None | |||
decoder_hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
decoder_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
cross_attentions: Optional[Tuple["torch.FloatTensor"]] = None | |||
encoder_last_hidden_state: Optional["torch.FloatTensor"] = None | |||
encoder_hidden_states: Optional[Tuple["torch.FloatTensor"]] = None | |||
encoder_attentions: Optional[Tuple["torch.FloatTensor"]] = None |
@@ -0,0 +1,5 @@ | |||
from .bart import * | |||
from .bert import * | |||
from .cpt import * | |||
from .gpt2 import * | |||
from .roberta import * |
@@ -0,0 +1,541 @@ | |||
# coding=utf-8 | |||
# Copyright 2018 The HuggingFace Inc. team. | |||
# | |||
# 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. | |||
""" Auto Config class. """ | |||
import importlib | |||
import re | |||
from collections import OrderedDict | |||
from typing import List, Union | |||
from fastNLP.transformers.torch.configuration_utils import PretrainedConfig | |||
from fastNLP.transformers.torch.file_utils import CONFIG_NAME | |||
from fastNLP.core.log import logger | |||
CONFIG_MAPPING_NAMES = OrderedDict( | |||
[ | |||
# Add configs here | |||
("fnet", "FNetConfig"), | |||
("gptj", "GPTJConfig"), | |||
("layoutlmv2", "LayoutLMv2Config"), | |||
("beit", "BeitConfig"), | |||
("rembert", "RemBertConfig"), | |||
("visual_bert", "VisualBertConfig"), | |||
("canine", "CanineConfig"), | |||
("roformer", "RoFormerConfig"), | |||
("clip", "CLIPConfig"), | |||
("bigbird_pegasus", "BigBirdPegasusConfig"), | |||
("deit", "DeiTConfig"), | |||
("luke", "LukeConfig"), | |||
("detr", "DetrConfig"), | |||
("gpt_neo", "GPTNeoConfig"), | |||
("big_bird", "BigBirdConfig"), | |||
("speech_to_text_2", "Speech2Text2Config"), | |||
("speech_to_text", "Speech2TextConfig"), | |||
("vit", "ViTConfig"), | |||
("wav2vec2", "Wav2Vec2Config"), | |||
("m2m_100", "M2M100Config"), | |||
("convbert", "ConvBertConfig"), | |||
("led", "LEDConfig"), | |||
("blenderbot-small", "BlenderbotSmallConfig"), | |||
("retribert", "RetriBertConfig"), | |||
("ibert", "IBertConfig"), | |||
("mt5", "MT5Config"), | |||
("t5", "T5Config"), | |||
("mobilebert", "MobileBertConfig"), | |||
("distilbert", "DistilBertConfig"), | |||
("albert", "AlbertConfig"), | |||
("bert-generation", "BertGenerationConfig"), | |||
("camembert", "CamembertConfig"), | |||
("xlm-roberta", "XLMRobertaConfig"), | |||
("pegasus", "PegasusConfig"), | |||
("marian", "MarianConfig"), | |||
("mbart", "MBartConfig"), | |||
("megatron-bert", "MegatronBertConfig"), | |||
("mpnet", "MPNetConfig"), | |||
("bart", "BartConfig"), | |||
("blenderbot", "BlenderbotConfig"), | |||
("reformer", "ReformerConfig"), | |||
("longformer", "LongformerConfig"), | |||
("roberta", "RobertaConfig"), | |||
("deberta-v2", "DebertaV2Config"), | |||
("deberta", "DebertaConfig"), | |||
("flaubert", "FlaubertConfig"), | |||
("fsmt", "FSMTConfig"), | |||
("squeezebert", "SqueezeBertConfig"), | |||
("hubert", "HubertConfig"), | |||
("bert", "BertConfig"), | |||
("openai-gpt", "OpenAIGPTConfig"), | |||
("gpt2", "GPT2Config"), | |||
("transfo-xl", "TransfoXLConfig"), | |||
("xlnet", "XLNetConfig"), | |||
("xlm-prophetnet", "XLMProphetNetConfig"), | |||
("prophetnet", "ProphetNetConfig"), | |||
("xlm", "XLMConfig"), | |||
("ctrl", "CTRLConfig"), | |||
("electra", "ElectraConfig"), | |||
("speech-encoder-decoder", "SpeechEncoderDecoderConfig"), | |||
("encoder-decoder", "EncoderDecoderConfig"), | |||
("funnel", "FunnelConfig"), | |||
("lxmert", "LxmertConfig"), | |||
("dpr", "DPRConfig"), | |||
("layoutlm", "LayoutLMConfig"), | |||
("rag", "RagConfig"), | |||
("tapas", "TapasConfig"), | |||
("splinter", "SplinterConfig"), | |||
] | |||
) | |||
CONFIG_ARCHIVE_MAP_MAPPING_NAMES = OrderedDict( | |||
[ | |||
# Add archive maps here | |||
("fnet", "FNET_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("pegasus", "PEGASUS_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("gptj", "GPTJ_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("layoutlmv2", "LAYOUTLMV2_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("beit", "BEIT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("rembert", "REMBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("visual_bert", "VISUAL_BERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("canine", "CANINE_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("roformer", "ROFORMER_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("clip", "CLIP_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("bigbird_pegasus", "BIGBIRD_PEGASUS_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("deit", "DEIT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("luke", "LUKE_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("detr", "DETR_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("gpt_neo", "GPT_NEO_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("big_bird", "BIG_BIRD_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("megatron-bert", "MEGATRON_BERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("speech_to_text", "SPEECH_TO_TEXT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("speech_to_text_2", "SPEECH_TO_TEXT_2_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("vit", "VIT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("wav2vec2", "WAV_2_VEC_2_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("m2m_100", "M2M_100_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("convbert", "CONVBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("led", "LED_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("blenderbot-small", "BLENDERBOT_SMALL_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("bert", "BERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("bart", "BART_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("blenderbot", "BLENDERBOT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("mbart", "MBART_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("openai-gpt", "OPENAI_GPT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("transfo-xl", "TRANSFO_XL_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("gpt2", "GPT2_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("ctrl", "CTRL_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("xlnet", "XLNET_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("xlm", "XLM_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("roberta", "ROBERTA_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("distilbert", "DISTILBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("albert", "ALBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("camembert", "CAMEMBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("t5", "T5_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("xlm-roberta", "XLM_ROBERTA_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("flaubert", "FLAUBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("fsmt", "FSMT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("electra", "ELECTRA_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("longformer", "LONGFORMER_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("retribert", "RETRIBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("funnel", "FUNNEL_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("lxmert", "LXMERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("layoutlm", "LAYOUTLM_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("dpr", "DPR_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("deberta", "DEBERTA_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("deberta-v2", "DEBERTA_V2_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("squeezebert", "SQUEEZEBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("xlm-prophetnet", "XLM_PROPHETNET_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("prophetnet", "PROPHETNET_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("mpnet", "MPNET_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("tapas", "TAPAS_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("ibert", "IBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("hubert", "HUBERT_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
("splinter", "SPLINTER_PRETRAINED_CONFIG_ARCHIVE_MAP"), | |||
] | |||
) | |||
MODEL_NAMES_MAPPING = OrderedDict( | |||
[ | |||
# Add full (and cased) model names here | |||
("fnet", "FNet"), | |||
("gptj", "GPT-J"), | |||
("beit", "BeiT"), | |||
("rembert", "RemBERT"), | |||
("layoutlmv2", "LayoutLMv2"), | |||
("visual_bert", "VisualBert"), | |||
("canine", "Canine"), | |||
("roformer", "RoFormer"), | |||
("clip", "CLIP"), | |||
("bigbird_pegasus", "BigBirdPegasus"), | |||
("deit", "DeiT"), | |||
("luke", "LUKE"), | |||
("detr", "DETR"), | |||
("gpt_neo", "GPT Neo"), | |||
("big_bird", "BigBird"), | |||
("speech_to_text_2", "Speech2Text2"), | |||
("speech_to_text", "Speech2Text"), | |||
("vit", "ViT"), | |||
("wav2vec2", "Wav2Vec2"), | |||
("m2m_100", "M2M100"), | |||
("convbert", "ConvBERT"), | |||
("led", "LED"), | |||
("blenderbot-small", "BlenderbotSmall"), | |||
("retribert", "RetriBERT"), | |||
("ibert", "I-BERT"), | |||
("t5", "T5"), | |||
("mobilebert", "MobileBERT"), | |||
("distilbert", "DistilBERT"), | |||
("albert", "ALBERT"), | |||
("bert-generation", "Bert Generation"), | |||
("camembert", "CamemBERT"), | |||
("xlm-roberta", "XLM-RoBERTa"), | |||
("pegasus", "Pegasus"), | |||
("blenderbot", "Blenderbot"), | |||
("marian", "Marian"), | |||
("mbart", "mBART"), | |||
("megatron-bert", "MegatronBert"), | |||
("bart", "BART"), | |||
("reformer", "Reformer"), | |||
("longformer", "Longformer"), | |||
("roberta", "RoBERTa"), | |||
("flaubert", "FlauBERT"), | |||
("fsmt", "FairSeq Machine-Translation"), | |||
("squeezebert", "SqueezeBERT"), | |||
("bert", "BERT"), | |||
("openai-gpt", "OpenAI GPT"), | |||
("gpt2", "OpenAI GPT-2"), | |||
("transfo-xl", "Transformer-XL"), | |||
("xlnet", "XLNet"), | |||
("xlm", "XLM"), | |||
("ctrl", "CTRL"), | |||
("electra", "ELECTRA"), | |||
("encoder-decoder", "Encoder decoder"), | |||
("speech-encoder-decoder", "Speech Encoder decoder"), | |||
("funnel", "Funnel Transformer"), | |||
("lxmert", "LXMERT"), | |||
("deberta-v2", "DeBERTa-v2"), | |||
("deberta", "DeBERTa"), | |||
("layoutlm", "LayoutLM"), | |||
("dpr", "DPR"), | |||
("rag", "RAG"), | |||
("xlm-prophetnet", "XLMProphetNet"), | |||
("prophetnet", "ProphetNet"), | |||
("mt5", "mT5"), | |||
("mpnet", "MPNet"), | |||
("tapas", "TAPAS"), | |||
("hubert", "Hubert"), | |||
("barthez", "BARThez"), | |||
("phobert", "PhoBERT"), | |||
("cpm", "CPM"), | |||
("bertweet", "Bertweet"), | |||
("bert-japanese", "BertJapanese"), | |||
("byt5", "ByT5"), | |||
("mbart50", "mBART-50"), | |||
("splinter", "Splinter"), | |||
] | |||
) | |||
SPECIAL_MODEL_TYPE_TO_MODULE_NAME = OrderedDict([("openai-gpt", "openai")]) | |||
def model_type_to_module_name(key): | |||
"""Converts a config key to the corresponding module.""" | |||
# Special treatment | |||
if key in SPECIAL_MODEL_TYPE_TO_MODULE_NAME: | |||
return SPECIAL_MODEL_TYPE_TO_MODULE_NAME[key] | |||
return key.replace("-", "_") | |||
def config_class_to_model_type(config): | |||
"""Converts a config class name to the corresponding model type""" | |||
for key, cls in CONFIG_MAPPING_NAMES.items(): | |||
if cls == config: | |||
return key | |||
return None | |||
class _LazyConfigMapping(OrderedDict): | |||
""" | |||
A dictionary that lazily load its values when they are requested. | |||
""" | |||
def __init__(self, mapping): | |||
self._mapping = mapping | |||
self._modules = {} | |||
def __getitem__(self, key): | |||
if key not in self._mapping: | |||
raise KeyError(key) | |||
value = self._mapping[key] | |||
module_name = model_type_to_module_name(key) | |||
if module_name not in self._modules: | |||
self._modules[module_name] = importlib.import_module(f".{module_name}", "transformers.models") | |||
return getattr(self._modules[module_name], value) | |||
def keys(self): | |||
return self._mapping.keys() | |||
def values(self): | |||
return [self[k] for k in self._mapping.keys()] | |||
def items(self): | |||
return [(k, self[k]) for k in self._mapping.keys()] | |||
def __iter__(self): | |||
return iter(self._mapping.keys()) | |||
def __contains__(self, item): | |||
return item in self._mapping | |||
CONFIG_MAPPING = _LazyConfigMapping(CONFIG_MAPPING_NAMES) | |||
class _LazyLoadAllMappings(OrderedDict): | |||
""" | |||
A mapping that will load all pairs of key values at the first access (either by indexing, requestions keys, values, | |||
etc.) | |||
Args: | |||
mapping: The mapping to load. | |||
""" | |||
def __init__(self, mapping): | |||
self._mapping = mapping | |||
self._initialized = False | |||
self._data = {} | |||
def _initialize(self): | |||
if self._initialized: | |||
return | |||
logger.warn( | |||
"ALL_PRETRAINED_CONFIG_ARCHIVE_MAP is deprecated and will be removed in v5 of Transformers. " | |||
"It does not contain all available model checkpoints, far from it. Checkout hf.co/models for that.", | |||
FutureWarning, | |||
) | |||
for model_type, map_name in self._mapping.items(): | |||
module_name = model_type_to_module_name(model_type) | |||
module = importlib.import_module(f".{module_name}", "transformers.models") | |||
mapping = getattr(module, map_name) | |||
self._data.update(mapping) | |||
self._initialized = True | |||
def __getitem__(self, key): | |||
self._initialize() | |||
return self._data[key] | |||
def keys(self): | |||
self._initialize() | |||
return self._data.keys() | |||
def values(self): | |||
self._initialize() | |||
return self._data.values() | |||
def items(self): | |||
self._initialize() | |||
return self._data.keys() | |||
def __iter__(self): | |||
self._initialize() | |||
return iter(self._data) | |||
def __contains__(self, item): | |||
self._initialize() | |||
return item in self._data | |||
ALL_PRETRAINED_CONFIG_ARCHIVE_MAP = _LazyLoadAllMappings(CONFIG_ARCHIVE_MAP_MAPPING_NAMES) | |||
def _get_class_name(model_class: Union[str, List[str]]): | |||
if isinstance(model_class, (list, tuple)): | |||
return " or ".join([f":class:`~transformers.{c}`" for c in model_class if c is not None]) | |||
return f":class:`~transformers.{model_class}`" | |||
def _list_model_options(indent, config_to_class=None, use_model_types=True): | |||
if config_to_class is None and not use_model_types: | |||
raise ValueError("Using `use_model_types=False` requires a `config_to_class` dictionary.") | |||
if use_model_types: | |||
if config_to_class is None: | |||
model_type_to_name = { | |||
model_type: f":class:`~transformers.{config}`" for model_type, config in CONFIG_MAPPING_NAMES.items() | |||
} | |||
else: | |||
model_type_to_name = { | |||
model_type: _get_class_name(model_class) | |||
for model_type, model_class in config_to_class.items() | |||
if model_type in MODEL_NAMES_MAPPING | |||
} | |||
lines = [ | |||
f"{indent}- **{model_type}** -- {model_type_to_name[model_type]} ({MODEL_NAMES_MAPPING[model_type]} model)" | |||
for model_type in sorted(model_type_to_name.keys()) | |||
] | |||
else: | |||
config_to_name = { | |||
CONFIG_MAPPING_NAMES[config]: _get_class_name(clas) | |||
for config, clas in config_to_class.items() | |||
if config in CONFIG_MAPPING_NAMES | |||
} | |||
config_to_model_name = { | |||
config: MODEL_NAMES_MAPPING[model_type] for model_type, config in CONFIG_MAPPING_NAMES.items() | |||
} | |||
lines = [ | |||
f"{indent}- :class:`~transformers.{config_name}` configuration class: {config_to_name[config_name]} ({config_to_model_name[config_name]} model)" | |||
for config_name in sorted(config_to_name.keys()) | |||
] | |||
return "\n".join(lines) | |||
def replace_list_option_in_docstrings(config_to_class=None, use_model_types=True): | |||
def docstring_decorator(fn): | |||
docstrings = fn.__doc__ | |||
lines = docstrings.split("\n") | |||
i = 0 | |||
while i < len(lines) and re.search(r"^(\s*)List options\s*$", lines[i]) is None: | |||
i += 1 | |||
if i < len(lines): | |||
indent = re.search(r"^(\s*)List options\s*$", lines[i]).groups()[0] | |||
if use_model_types: | |||
indent = f"{indent} " | |||
lines[i] = _list_model_options(indent, config_to_class=config_to_class, use_model_types=use_model_types) | |||
docstrings = "\n".join(lines) | |||
else: | |||
raise ValueError( | |||
f"The function {fn} should have an empty 'List options' in its docstring as placeholder, current docstring is:\n{docstrings}" | |||
) | |||
fn.__doc__ = docstrings | |||
return fn | |||
return docstring_decorator | |||
class AutoConfig: | |||
r""" | |||
This is a generic configuration class that will be instantiated as one of the configuration classes of the library | |||
when created with the :meth:`~transformers.AutoConfig.from_pretrained` class method. | |||
This class cannot be instantiated directly using ``__init__()`` (throws an error). | |||
""" | |||
def __init__(self): | |||
raise EnvironmentError( | |||
"AutoConfig is designed to be instantiated " | |||
"using the `AutoConfig.from_pretrained(pretrained_model_name_or_path)` method." | |||
) | |||
@classmethod | |||
def for_model(cls, model_type: str, *args, **kwargs): | |||
if model_type in CONFIG_MAPPING: | |||
config_class = CONFIG_MAPPING[model_type] | |||
return config_class(*args, **kwargs) | |||
raise ValueError( | |||
f"Unrecognized model identifier: {model_type}. Should contain one of {', '.join(CONFIG_MAPPING.keys())}" | |||
) | |||
@classmethod | |||
@replace_list_option_in_docstrings() | |||
def from_pretrained(cls, pretrained_model_name_or_path, **kwargs): | |||
r""" | |||
Instantiate one of the configuration classes of the library from a pretrained model configuration. | |||
The configuration class to instantiate is selected based on the :obj:`model_type` property of the config object | |||
that is loaded, or when it's missing, by falling back to using pattern matching on | |||
:obj:`pretrained_model_name_or_path`: | |||
List options | |||
Args: | |||
pretrained_model_name_or_path (:obj:`str` or :obj:`os.PathLike`): | |||
Can be either: | |||
- A string, the `model id` of a pretrained model configuration hosted inside a model repo on | |||
huggingface.co. Valid model ids can be located at the root-level, like ``bert-base-uncased``, or | |||
namespaced under a user or organization name, like ``dbmdz/bert-base-german-cased``. | |||
- A path to a `directory` containing a configuration file saved using the | |||
:meth:`~transformers.PretrainedConfig.save_pretrained` method, or the | |||
:meth:`~transformers.PreTrainedModel.save_pretrained` method, e.g., ``./my_model_directory/``. | |||
- A path or url to a saved configuration JSON `file`, e.g., | |||
``./my_model_directory/configuration.json``. | |||
cache_dir (:obj:`str` or :obj:`os.PathLike`, `optional`): | |||
Path to a directory in which a downloaded pretrained model configuration should be cached if the | |||
standard cache should not be used. | |||
force_download (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not to force the (re-)download the model weights and configuration files and override the | |||
cached versions if they exist. | |||
resume_download (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not to delete incompletely received files. Will attempt to resume the download if such a | |||
file exists. | |||
proxies (:obj:`Dict[str, str]`, `optional`): | |||
A dictionary of proxy servers to use by protocol or endpoint, e.g., :obj:`{'http': 'foo.bar:3128', | |||
'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request. | |||
revision(:obj:`str`, `optional`, defaults to :obj:`"main"`): | |||
The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a | |||
git-based system for storing models and other artifacts on huggingface.co, so ``revision`` can be any | |||
identifier allowed by git. | |||
return_unused_kwargs (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
If :obj:`False`, then this function returns just the final configuration object. | |||
If :obj:`True`, then this functions returns a :obj:`Tuple(config, unused_kwargs)` where `unused_kwargs` | |||
is a dictionary consisting of the key/value pairs whose keys are not configuration attributes: i.e., | |||
the part of ``kwargs`` which has not been used to update ``config`` and is otherwise ignored. | |||
kwargs(additional keyword arguments, `optional`): | |||
The values in kwargs of any keys which are configuration attributes will be used to override the loaded | |||
values. Behavior concerning key/value pairs whose keys are *not* configuration attributes is controlled | |||
by the ``return_unused_kwargs`` keyword parameter. | |||
Examples:: | |||
>>> from transformers import AutoConfig | |||
>>> # Download configuration from huggingface.co and cache. | |||
>>> config = AutoConfig.from_pretrained('bert-base-uncased') | |||
>>> # Download configuration from huggingface.co (user-uploaded) and cache. | |||
>>> config = AutoConfig.from_pretrained('dbmdz/bert-base-german-cased') | |||
>>> # If configuration file is in a directory (e.g., was saved using `save_pretrained('./test/saved_model/')`). | |||
>>> config = AutoConfig.from_pretrained('./test/bert_saved_model/') | |||
>>> # Load a specific configuration file. | |||
>>> config = AutoConfig.from_pretrained('./test/bert_saved_model/my_configuration.json') | |||
>>> # Change some config attributes when loading a pretrained config. | |||
>>> config = AutoConfig.from_pretrained('bert-base-uncased', output_attentions=True, foo=False) | |||
>>> config.output_attentions | |||
True | |||
>>> config, unused_kwargs = AutoConfig.from_pretrained('bert-base-uncased', output_attentions=True, foo=False, return_unused_kwargs=True) | |||
>>> config.output_attentions | |||
True | |||
>>> config.unused_kwargs | |||
{'foo': False} | |||
""" | |||
kwargs["_from_auto"] = True | |||
config_dict, _ = PretrainedConfig.get_config_dict(pretrained_model_name_or_path, **kwargs) | |||
if "model_type" in config_dict: | |||
config_class = CONFIG_MAPPING[config_dict["model_type"]] | |||
return config_class.from_dict(config_dict, **kwargs) | |||
else: | |||
# Fallback: use pattern matching on the string. | |||
for pattern, config_class in CONFIG_MAPPING.items(): | |||
if pattern in str(pretrained_model_name_or_path): | |||
return config_class.from_dict(config_dict, **kwargs) | |||
raise ValueError( | |||
f"Unrecognized model in {pretrained_model_name_or_path}. " | |||
f"Should have a `model_type` key in its {CONFIG_NAME}, or contain one of the following strings " | |||
f"in its name: {', '.join(CONFIG_MAPPING.keys())}" | |||
) |
@@ -0,0 +1,199 @@ | |||
# coding=utf-8 | |||
# Copyright 2018 The HuggingFace Inc. team. | |||
# | |||
# 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. | |||
""" Auto Tokenizer class. """ | |||
from collections import OrderedDict | |||
from typing import TYPE_CHECKING, Dict, Optional, Tuple, Union | |||
from ...file_utils import ( | |||
is_sentencepiece_available, | |||
is_tokenizers_available, | |||
) | |||
if TYPE_CHECKING: | |||
# This significantly improves completion suggestion performance when | |||
# the transformers package is used with Microsoft's Pylance language server. | |||
TOKENIZER_MAPPING_NAMES: OrderedDict[str, Tuple[Optional[str], Optional[str]]] = OrderedDict() | |||
else: | |||
TOKENIZER_MAPPING_NAMES = OrderedDict( | |||
[ | |||
("fnet", ("FNetTokenizer", "FNetTokenizerFast" if is_tokenizers_available() else None)), | |||
("retribert", ("RetriBertTokenizer", "RetriBertTokenizerFast" if is_tokenizers_available() else None)), | |||
("roformer", ("RoFormerTokenizer", "RoFormerTokenizerFast" if is_tokenizers_available() else None)), | |||
( | |||
"t5", | |||
( | |||
"T5Tokenizer" if is_sentencepiece_available() else None, | |||
"T5TokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
( | |||
"mt5", | |||
( | |||
"MT5Tokenizer" if is_sentencepiece_available() else None, | |||
"MT5TokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
("mobilebert", ("MobileBertTokenizer", "MobileBertTokenizerFast" if is_tokenizers_available() else None)), | |||
("distilbert", ("DistilBertTokenizer", "DistilBertTokenizerFast" if is_tokenizers_available() else None)), | |||
( | |||
"albert", | |||
( | |||
"AlbertTokenizer" if is_sentencepiece_available() else None, | |||
"AlbertTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
( | |||
"camembert", | |||
( | |||
"CamembertTokenizer" if is_sentencepiece_available() else None, | |||
"CamembertTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
( | |||
"pegasus", | |||
( | |||
"PegasusTokenizer" if is_sentencepiece_available() else None, | |||
"PegasusTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
( | |||
"mbart", | |||
( | |||
"MBartTokenizer" if is_sentencepiece_available() else None, | |||
"MBartTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
( | |||
"xlm-roberta", | |||
( | |||
"XLMRobertaTokenizer" if is_sentencepiece_available() else None, | |||
"XLMRobertaTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
("marian", ("MarianTokenizer" if is_sentencepiece_available() else None, None)), | |||
("blenderbot-small", ("BlenderbotSmallTokenizer", None)), | |||
("blenderbot", ("BlenderbotTokenizer", None)), | |||
("bart", ("BartTokenizer", "BartTokenizerFast")), | |||
("longformer", ("LongformerTokenizer", "LongformerTokenizerFast" if is_tokenizers_available() else None)), | |||
("roberta", ("RobertaTokenizer", "RobertaTokenizerFast" if is_tokenizers_available() else None)), | |||
( | |||
"reformer", | |||
( | |||
"ReformerTokenizer" if is_sentencepiece_available() else None, | |||
"ReformerTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
("electra", ("ElectraTokenizer", "ElectraTokenizerFast" if is_tokenizers_available() else None)), | |||
("funnel", ("FunnelTokenizer", "FunnelTokenizerFast" if is_tokenizers_available() else None)), | |||
("lxmert", ("LxmertTokenizer", "LxmertTokenizerFast" if is_tokenizers_available() else None)), | |||
("layoutlm", ("LayoutLMTokenizer", "LayoutLMTokenizerFast" if is_tokenizers_available() else None)), | |||
("layoutlmv2", ("LayoutLMv2Tokenizer", "LayoutLMv2TokenizerFast" if is_tokenizers_available() else None)), | |||
( | |||
"dpr", | |||
( | |||
"DPRQuestionEncoderTokenizer", | |||
"DPRQuestionEncoderTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
( | |||
"squeezebert", | |||
("SqueezeBertTokenizer", "SqueezeBertTokenizerFast" if is_tokenizers_available() else None), | |||
), | |||
("bert", ("BertTokenizer", "BertTokenizerFast" if is_tokenizers_available() else None)), | |||
("openai-gpt", ("OpenAIGPTTokenizer", "OpenAIGPTTokenizerFast" if is_tokenizers_available() else None)), | |||
("gpt2", ("GPT2Tokenizer", "GPT2TokenizerFast" if is_tokenizers_available() else None)), | |||
("transfo-xl", ("TransfoXLTokenizer", None)), | |||
( | |||
"xlnet", | |||
( | |||
"XLNetTokenizer" if is_sentencepiece_available() else None, | |||
"XLNetTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
("flaubert", ("FlaubertTokenizer", None)), | |||
("xlm", ("XLMTokenizer", None)), | |||
("ctrl", ("CTRLTokenizer", None)), | |||
("fsmt", ("FSMTTokenizer", None)), | |||
("bert-generation", ("BertGenerationTokenizer" if is_sentencepiece_available() else None, None)), | |||
("deberta", ("DebertaTokenizer", "DebertaTokenizerFast" if is_tokenizers_available() else None)), | |||
("deberta-v2", ("DebertaV2Tokenizer" if is_sentencepiece_available() else None, None)), | |||
("rag", ("RagTokenizer", None)), | |||
("xlm-prophetnet", ("XLMProphetNetTokenizer" if is_sentencepiece_available() else None, None)), | |||
("speech_to_text", ("Speech2TextTokenizer" if is_sentencepiece_available() else None, None)), | |||
("speech_to_text_2", ("Speech2Text2Tokenizer", None)), | |||
("m2m_100", ("M2M100Tokenizer" if is_sentencepiece_available() else None, None)), | |||
("prophetnet", ("ProphetNetTokenizer", None)), | |||
("mpnet", ("MPNetTokenizer", "MPNetTokenizerFast" if is_tokenizers_available() else None)), | |||
("tapas", ("TapasTokenizer", None)), | |||
("led", ("LEDTokenizer", "LEDTokenizerFast" if is_tokenizers_available() else None)), | |||
("convbert", ("ConvBertTokenizer", "ConvBertTokenizerFast" if is_tokenizers_available() else None)), | |||
( | |||
"big_bird", | |||
( | |||
"BigBirdTokenizer" if is_sentencepiece_available() else None, | |||
"BigBirdTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
("ibert", ("RobertaTokenizer", "RobertaTokenizerFast" if is_tokenizers_available() else None)), | |||
("wav2vec2", ("Wav2Vec2CTCTokenizer", None)), | |||
("hubert", ("Wav2Vec2CTCTokenizer", None)), | |||
("gpt_neo", ("GPT2Tokenizer", "GPT2TokenizerFast" if is_tokenizers_available() else None)), | |||
("luke", ("LukeTokenizer", None)), | |||
("bigbird_pegasus", ("PegasusTokenizer", "PegasusTokenizerFast" if is_tokenizers_available() else None)), | |||
("canine", ("CanineTokenizer", None)), | |||
("bertweet", ("BertweetTokenizer", None)), | |||
("bert-japanese", ("BertJapaneseTokenizer", None)), | |||
("splinter", ("SplinterTokenizer", "SplinterTokenizerFast")), | |||
("byt5", ("ByT5Tokenizer", None)), | |||
( | |||
"cpm", | |||
( | |||
"CpmTokenizer" if is_sentencepiece_available() else None, | |||
"CpmTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
("herbert", ("HerbertTokenizer", "HerbertTokenizerFast" if is_tokenizers_available() else None)), | |||
("phobert", ("PhobertTokenizer", None)), | |||
( | |||
"barthez", | |||
( | |||
"BarthezTokenizer" if is_sentencepiece_available() else None, | |||
"BarthezTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
( | |||
"mbart50", | |||
( | |||
"MBart50Tokenizer" if is_sentencepiece_available() else None, | |||
"MBart50TokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
( | |||
"rembert", | |||
( | |||
"RemBertTokenizer" if is_sentencepiece_available() else None, | |||
"RemBertTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
( | |||
"clip", | |||
( | |||
"CLIPTokenizer", | |||
"CLIPTokenizerFast" if is_tokenizers_available() else None, | |||
), | |||
), | |||
] | |||
) |
@@ -0,0 +1,20 @@ | |||
__all__ = [ | |||
"BartConfig", | |||
"BART_PRETRAINED_CONFIG_ARCHIVE_MAP", | |||
"BART_PRETRAINED_MODEL_ARCHIVE_LIST", | |||
"BartForCausalLM", | |||
"BartForConditionalGeneration", | |||
"BartForQuestionAnswering", | |||
"BartForSequenceClassification", | |||
"BartModel", | |||
"BartPretrainedModel", | |||
"PretrainedBartModel", | |||
"BartTokenizer", | |||
] | |||
from .configuration_bart import BartConfig, BART_PRETRAINED_CONFIG_ARCHIVE_MAP | |||
from .tokenization_bart import BartTokenizer | |||
from .modeling_bart import BartForCausalLM, BartForConditionalGeneration, BartModel, BartForQuestionAnswering, \ | |||
BartForSequenceClassification, BartPretrainedModel, PretrainedBartModel, BART_PRETRAINED_MODEL_ARCHIVE_LIST |
@@ -0,0 +1,177 @@ | |||
# coding=utf-8 | |||
# Copyright 2021 The Fairseq Authors and The HuggingFace Inc. team. 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. | |||
""" BART model configuration """ | |||
from fastNLP.transformers.torch.configuration_utils import PretrainedConfig | |||
from fastNLP.core.log import logger | |||
__all__ = [ | |||
"BartConfig", | |||
"BART_PRETRAINED_CONFIG_ARCHIVE_MAP", | |||
] | |||
BART_PRETRAINED_CONFIG_ARCHIVE_MAP = { | |||
"facebook/bart-large": "https://huggingface.co/facebook/bart-large/resolve/main/config.json", | |||
# See all BART models at https://huggingface.co/models?filter=bart | |||
} | |||
class BartConfig(PretrainedConfig): | |||
r""" | |||
This is the configuration class to store the configuration of a :class:`~transformers.BartModel`. It is used to | |||
instantiate a BART model according to the specified arguments, defining the model architecture. Instantiating a | |||
configuration with the defaults will yield a similar configuration to that of the BART `facebook/bart-large | |||
<https://huggingface.co/facebook/bart-large>`__ 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 50265): | |||
Vocabulary size of the BART model. Defines the number of different tokens that can be represented by the | |||
:obj:`inputs_ids` passed when calling :class:`~transformers.BartModel` or | |||
:class:`~transformers.TFBartModel`. | |||
d_model (:obj:`int`, `optional`, defaults to 1024): | |||
Dimensionality of the layers and the pooler layer. | |||
encoder_layers (:obj:`int`, `optional`, defaults to 12): | |||
Number of encoder layers. | |||
decoder_layers (:obj:`int`, `optional`, defaults to 12): | |||
Number of decoder layers. | |||
encoder_attention_heads (:obj:`int`, `optional`, defaults to 16): | |||
Number of attention heads for each attention layer in the Transformer encoder. | |||
decoder_attention_heads (:obj:`int`, `optional`, defaults to 16): | |||
Number of attention heads for each attention layer in the Transformer decoder. | |||
decoder_ffn_dim (:obj:`int`, `optional`, defaults to 4096): | |||
Dimensionality of the "intermediate" (often named feed-forward) layer in decoder. | |||
encoder_ffn_dim (:obj:`int`, `optional`, defaults to 4096): | |||
Dimensionality of the "intermediate" (often named feed-forward) layer in decoder. | |||
activation_function (:obj:`str` or :obj:`function`, `optional`, defaults to :obj:`"gelu"`): | |||
The non-linear activation function (function or string) in the encoder and pooler. If string, | |||
:obj:`"gelu"`, :obj:`"relu"`, :obj:`"silu"` and :obj:`"gelu_new"` are supported. | |||
dropout (:obj:`float`, `optional`, defaults to 0.1): | |||
The dropout probability for all fully connected layers in the embeddings, encoder, and pooler. | |||
attention_dropout (:obj:`float`, `optional`, defaults to 0.0): | |||
The dropout ratio for the attention probabilities. | |||
activation_dropout (:obj:`float`, `optional`, defaults to 0.0): | |||
The dropout ratio for activations inside the fully connected layer. | |||
classifier_dropout (:obj:`float`, `optional`, defaults to 0.0): | |||
The dropout ratio for classifier. | |||
max_position_embeddings (:obj:`int`, `optional`, defaults to 1024): | |||
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). | |||
init_std (:obj:`float`, `optional`, defaults to 0.02): | |||
The standard deviation of the truncated_normal_initializer for initializing all weight matrices. | |||
encoder_layerdrop: (:obj:`float`, `optional`, defaults to 0.0): | |||
The LayerDrop probability for the encoder. See the `LayerDrop paper <see | |||
https://arxiv.org/abs/1909.11556>`__ for more details. | |||
decoder_layerdrop: (:obj:`float`, `optional`, defaults to 0.0): | |||
The LayerDrop probability for the decoder. See the `LayerDrop paper <see | |||
https://arxiv.org/abs/1909.11556>`__ for more details. | |||
scale_embedding (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Scale embeddings by diving by sqrt(d_model). | |||
use_cache (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Whether or not the model should return the last key/values attentions (not used by all models). | |||
num_labels: (:obj:`int`, `optional`, defaults to 3): | |||
The number of labels to use in :class:`~transformers.BartForSequenceClassification`. | |||
forced_eos_token_id (:obj:`int`, `optional`, defaults to 2): | |||
The id of the token to force as the last generated token when :obj:`max_length` is reached. Usually set to | |||
:obj:`eos_token_id`. | |||
Example:: | |||
>>> from transformers import BartModel, BartConfig | |||
>>> # Initializing a BART facebook/bart-large style configuration | |||
>>> configuration = BartConfig() | |||
>>> # Initializing a model from the facebook/bart-large style configuration | |||
>>> model = BartModel(configuration) | |||
>>> # Accessing the model configuration | |||
>>> configuration = model.config | |||
""" | |||
model_type = "bart" | |||
keys_to_ignore_at_inference = ["past_key_values"] | |||
attribute_map = {"num_attention_heads": "encoder_attention_heads", "hidden_size": "d_model"} | |||
def __init__( | |||
self, | |||
vocab_size=50265, | |||
max_position_embeddings=1024, | |||
encoder_layers=12, | |||
encoder_ffn_dim=4096, | |||
encoder_attention_heads=16, | |||
decoder_layers=12, | |||
decoder_ffn_dim=4096, | |||
decoder_attention_heads=16, | |||
encoder_layerdrop=0.0, | |||
decoder_layerdrop=0.0, | |||
activation_function="gelu", | |||
d_model=1024, | |||
dropout=0.1, | |||
attention_dropout=0.0, | |||
activation_dropout=0.0, | |||
init_std=0.02, | |||
classifier_dropout=0.0, | |||
scale_embedding=False, | |||
use_cache=True, | |||
num_labels=3, | |||
pad_token_id=1, | |||
bos_token_id=0, | |||
eos_token_id=2, | |||
is_encoder_decoder=True, | |||
decoder_start_token_id=2, | |||
forced_eos_token_id=2, | |||
**kwargs | |||
): | |||
self.vocab_size = vocab_size | |||
self.max_position_embeddings = max_position_embeddings | |||
self.d_model = d_model | |||
self.encoder_ffn_dim = encoder_ffn_dim | |||
self.encoder_layers = encoder_layers | |||
self.encoder_attention_heads = encoder_attention_heads | |||
self.decoder_ffn_dim = decoder_ffn_dim | |||
self.decoder_layers = decoder_layers | |||
self.decoder_attention_heads = decoder_attention_heads | |||
self.dropout = dropout | |||
self.attention_dropout = attention_dropout | |||
self.activation_dropout = activation_dropout | |||
self.activation_function = activation_function | |||
self.init_std = init_std | |||
self.encoder_layerdrop = encoder_layerdrop | |||
self.decoder_layerdrop = decoder_layerdrop | |||
self.classifier_dropout = classifier_dropout | |||
self.use_cache = use_cache | |||
self.num_hidden_layers = encoder_layers | |||
self.scale_embedding = scale_embedding # scale factor will be sqrt(d_model) if True | |||
super().__init__( | |||
num_labels=num_labels, | |||
pad_token_id=pad_token_id, | |||
bos_token_id=bos_token_id, | |||
eos_token_id=eos_token_id, | |||
is_encoder_decoder=is_encoder_decoder, | |||
decoder_start_token_id=decoder_start_token_id, | |||
forced_eos_token_id=forced_eos_token_id, | |||
**kwargs, | |||
) | |||
# ensure backward compatibility for BART CNN models | |||
if self.forced_bos_token_id is None and kwargs.get("force_bos_token_to_be_generated", False): | |||
self.forced_bos_token_id = self.bos_token_id | |||
logger.warn( | |||
f"Please make sure the config includes `forced_bos_token_id={self.bos_token_id}` in future versions." | |||
"The config can simply be saved and uploaded again to be fixed." | |||
) |
@@ -0,0 +1,65 @@ | |||
# coding=utf-8 | |||
# Copyright 2020 The Facebook AI Research Team Authors and The HuggingFace Inc. team. | |||
# | |||
# 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. | |||
from ..roberta.tokenization_roberta import RobertaTokenizer | |||
from fastNLP.core.log import logger | |||
__all__ = [ | |||
"BartTokenizer", | |||
] | |||
VOCAB_FILES_NAMES = {"vocab_file": "vocab.json", "merges_file": "merges.txt"} | |||
# See all BART models at https://huggingface.co/models?filter=bart | |||
PRETRAINED_VOCAB_FILES_MAP = { | |||
"vocab_file": { | |||
"facebook/bart-base": "https://huggingface.co/facebook/bart-base/resolve/main/vocab.json", | |||
"facebook/bart-large": "https://huggingface.co/facebook/bart-large/resolve/main/vocab.json", | |||
"facebook/bart-large-mnli": "https://huggingface.co/facebook/bart-large-mnli/resolve/main/vocab.json", | |||
"facebook/bart-large-cnn": "https://huggingface.co/facebook/bart-large-cnn/resolve/main/vocab.json", | |||
"facebook/bart-large-xsum": "https://huggingface.co/facebook/bart-large-xsum/resolve/main/vocab.json", | |||
"yjernite/bart_eli5": "https://huggingface.co/yjernite/bart_eli5/resolve/main/vocab.json", | |||
}, | |||
"merges_file": { | |||
"facebook/bart-base": "https://huggingface.co/facebook/bart-base/resolve/main/merges.txt", | |||
"facebook/bart-large": "https://huggingface.co/facebook/bart-large/resolve/main/merges.txt", | |||
"facebook/bart-large-mnli": "https://huggingface.co/facebook/bart-large-mnli/resolve/main/merges.txt", | |||
"facebook/bart-large-cnn": "https://huggingface.co/facebook/bart-large-cnn/resolve/main/merges.txt", | |||
"facebook/bart-large-xsum": "https://huggingface.co/facebook/bart-large-xsum/resolve/main/merges.txt", | |||
"yjernite/bart_eli5": "https://huggingface.co/yjernite/bart_eli5/resolve/main/merges.txt", | |||
}, | |||
} | |||
PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES = { | |||
"facebook/bart-base": 1024, | |||
"facebook/bart-large": 1024, | |||
"facebook/bart-large-mnli": 1024, | |||
"facebook/bart-large-cnn": 1024, | |||
"facebook/bart-large-xsum": 1024, | |||
"yjernite/bart_eli5": 1024, | |||
} | |||
class BartTokenizer(RobertaTokenizer): | |||
r""" | |||
Construct a BART tokenizer. | |||
:class:`~transformers.BartTokenizer` is identical to :class:`~transformers.RobertaTokenizer`. Refer to superclass | |||
:class:`~transformers.RobertaTokenizer` for usage examples and documentation concerning the initialization | |||
parameters and other methods. | |||
""" | |||
vocab_files_names = VOCAB_FILES_NAMES | |||
pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP | |||
max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES |
@@ -0,0 +1,27 @@ | |||
__all__ = [ | |||
"BERT_PRETRAINED_CONFIG_ARCHIVE_MAP", | |||
"BertConfig", | |||
"BERT_PRETRAINED_MODEL_ARCHIVE_LIST", | |||
"BertForMaskedLM", | |||
"BertForMultipleChoice", | |||
"BertForNextSentencePrediction", | |||
"BertForPreTraining", | |||
"BertForQuestionAnswering", | |||
"BertForSequenceClassification", | |||
"BertForTokenClassification", | |||
"BertLayer", | |||
"BertLMHeadModel", | |||
"BertModel", | |||
"BertPreTrainedModel", | |||
"BasicTokenizer", | |||
"BertTokenizer", | |||
"WordpieceTokenizer", | |||
] | |||
from .configuration_bert import BertConfig, BERT_PRETRAINED_CONFIG_ARCHIVE_MAP | |||
from .tokenization_bert import BasicTokenizer, BertTokenizer, WordpieceTokenizer | |||
from .modeling_bert import BERT_PRETRAINED_MODEL_ARCHIVE_LIST, BertForMaskedLM, BertForMultipleChoice, BertForPreTraining, \ | |||
BertForNextSentencePrediction, BertForQuestionAnswering, BertForSequenceClassification, BertForTokenClassification, \ | |||
BertLayer, BertLMHeadModel, BertModel, BertPreTrainedModel |
@@ -0,0 +1,158 @@ | |||
# 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. | |||
""" BERT model configuration """ | |||
from fastNLP.transformers.torch.configuration_utils import PretrainedConfig | |||
from fastNLP.core.log import logger | |||
__all__ = [ | |||
"BERT_PRETRAINED_CONFIG_ARCHIVE_MAP", | |||
"BertConfig", | |||
] | |||
BERT_PRETRAINED_CONFIG_ARCHIVE_MAP = { | |||
"bert-base-uncased": "https://huggingface.co/bert-base-uncased/resolve/main/config.json", | |||
"bert-large-uncased": "https://huggingface.co/bert-large-uncased/resolve/main/config.json", | |||
"bert-base-cased": "https://huggingface.co/bert-base-cased/resolve/main/config.json", | |||
"bert-large-cased": "https://huggingface.co/bert-large-cased/resolve/main/config.json", | |||
"bert-base-multilingual-uncased": "https://huggingface.co/bert-base-multilingual-uncased/resolve/main/config.json", | |||
"bert-base-multilingual-cased": "https://huggingface.co/bert-base-multilingual-cased/resolve/main/config.json", | |||
"bert-base-chinese": "https://huggingface.co/bert-base-chinese/resolve/main/config.json", | |||
"bert-base-german-cased": "https://huggingface.co/bert-base-german-cased/resolve/main/config.json", | |||
"bert-large-uncased-whole-word-masking": "https://huggingface.co/bert-large-uncased-whole-word-masking/resolve/main/config.json", | |||
"bert-large-cased-whole-word-masking": "https://huggingface.co/bert-large-cased-whole-word-masking/resolve/main/config.json", | |||
"bert-large-uncased-whole-word-masking-finetuned-squad": "https://huggingface.co/bert-large-uncased-whole-word-masking-finetuned-squad/resolve/main/config.json", | |||
"bert-large-cased-whole-word-masking-finetuned-squad": "https://huggingface.co/bert-large-cased-whole-word-masking-finetuned-squad/resolve/main/config.json", | |||
"bert-base-cased-finetuned-mrpc": "https://huggingface.co/bert-base-cased-finetuned-mrpc/resolve/main/config.json", | |||
"bert-base-german-dbmdz-cased": "https://huggingface.co/bert-base-german-dbmdz-cased/resolve/main/config.json", | |||
"bert-base-german-dbmdz-uncased": "https://huggingface.co/bert-base-german-dbmdz-uncased/resolve/main/config.json", | |||
"cl-tohoku/bert-base-japanese": "https://huggingface.co/cl-tohoku/bert-base-japanese/resolve/main/config.json", | |||
"cl-tohoku/bert-base-japanese-whole-word-masking": "https://huggingface.co/cl-tohoku/bert-base-japanese-whole-word-masking/resolve/main/config.json", | |||
"cl-tohoku/bert-base-japanese-char": "https://huggingface.co/cl-tohoku/bert-base-japanese-char/resolve/main/config.json", | |||
"cl-tohoku/bert-base-japanese-char-whole-word-masking": "https://huggingface.co/cl-tohoku/bert-base-japanese-char-whole-word-masking/resolve/main/config.json", | |||
"TurkuNLP/bert-base-finnish-cased-v1": "https://huggingface.co/TurkuNLP/bert-base-finnish-cased-v1/resolve/main/config.json", | |||
"TurkuNLP/bert-base-finnish-uncased-v1": "https://huggingface.co/TurkuNLP/bert-base-finnish-uncased-v1/resolve/main/config.json", | |||
"wietsedv/bert-base-dutch-cased": "https://huggingface.co/wietsedv/bert-base-dutch-cased/resolve/main/config.json", | |||
# See all BERT models at https://huggingface.co/models?filter=bert | |||
} | |||
class BertConfig(PretrainedConfig): | |||
r""" | |||
This is the configuration class to store the configuration of a :class:`~transformers.BertModel` or a | |||
:class:`~transformers.TFBertModel`. It is used to instantiate a BERT model according to the specified arguments, | |||
defining the model architecture. Instantiating a configuration with the defaults will yield a similar configuration | |||
to that of the BERT `bert-base-uncased <https://huggingface.co/bert-base-uncased>`__ 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 number of different tokens that can be represented by the | |||
:obj:`inputs_ids` passed when calling :class:`~transformers.BertModel` or | |||
:class:`~transformers.TFBertModel`. | |||
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" (often named feed-forward) layer in the Transformer encoder. | |||
hidden_act (:obj:`str` or :obj:`Callable`, `optional`, defaults to :obj:`"gelu"`): | |||
The non-linear activation function (function or string) in the encoder and pooler. If string, | |||
:obj:`"gelu"`, :obj:`"relu"`, :obj:`"silu"` and :obj:`"gelu_new"` are supported. | |||
hidden_dropout_prob (:obj:`float`, `optional`, defaults to 0.1): | |||
The dropout probability 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 :obj:`token_type_ids` passed when calling :class:`~transformers.BertModel` or | |||
:class:`~transformers.TFBertModel`. | |||
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. | |||
position_embedding_type (:obj:`str`, `optional`, defaults to :obj:`"absolute"`): | |||
Type of position embedding. Choose one of :obj:`"absolute"`, :obj:`"relative_key"`, | |||
:obj:`"relative_key_query"`. For positional embeddings use :obj:`"absolute"`. For more information on | |||
:obj:`"relative_key"`, please refer to `Self-Attention with Relative Position Representations (Shaw et al.) | |||
<https://arxiv.org/abs/1803.02155>`__. For more information on :obj:`"relative_key_query"`, please refer to | |||
`Method 4` in `Improve Transformer Models with Better Relative Position Embeddings (Huang et al.) | |||
<https://arxiv.org/abs/2009.13658>`__. | |||
use_cache (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Whether or not the model should return the last key/values attentions (not used by all models). Only | |||
relevant if ``config.is_decoder=True``. | |||
classifier_dropout (:obj:`float`, `optional`): | |||
The dropout ratio for the classification head. | |||
Examples:: | |||
>>> from transformers import BertModel, BertConfig | |||
>>> # Initializing a BERT bert-base-uncased style configuration | |||
>>> configuration = BertConfig() | |||
>>> # Initializing a model from the bert-base-uncased style configuration | |||
>>> model = BertModel(configuration) | |||
>>> # Accessing the model configuration | |||
>>> configuration = model.config | |||
""" | |||
model_type = "bert" | |||
def __init__( | |||
self, | |||
vocab_size=30522, | |||
hidden_size=768, | |||
num_hidden_layers=12, | |||
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, | |||
pad_token_id=0, | |||
position_embedding_type="absolute", | |||
use_cache=True, | |||
classifier_dropout=None, | |||
**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.position_embedding_type = position_embedding_type | |||
self.use_cache = use_cache | |||
self.classifier_dropout = classifier_dropout |
@@ -0,0 +1,558 @@ | |||
# coding=utf-8 | |||
# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. | |||
# | |||
# 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. | |||
"""Tokenization classes for Bert.""" | |||
import collections | |||
import os | |||
import unicodedata | |||
from typing import List, Optional, Tuple | |||
from fastNLP.transformers.torch.tokenization_utils import PreTrainedTokenizer, _is_control, _is_punctuation, _is_whitespace | |||
from fastNLP.core.log import logger | |||
__all__ = [ | |||
"BasicTokenizer", | |||
"BertTokenizer", | |||
"WordpieceTokenizer", | |||
] | |||
VOCAB_FILES_NAMES = {"vocab_file": "vocab.txt"} | |||
PRETRAINED_VOCAB_FILES_MAP = { | |||
"vocab_file": { | |||
"bert-base-uncased": "https://huggingface.co/bert-base-uncased/resolve/main/vocab.txt", | |||
"bert-large-uncased": "https://huggingface.co/bert-large-uncased/resolve/main/vocab.txt", | |||
"bert-base-cased": "https://huggingface.co/bert-base-cased/resolve/main/vocab.txt", | |||
"bert-large-cased": "https://huggingface.co/bert-large-cased/resolve/main/vocab.txt", | |||
"bert-base-multilingual-uncased": "https://huggingface.co/bert-base-multilingual-uncased/resolve/main/vocab.txt", | |||
"bert-base-multilingual-cased": "https://huggingface.co/bert-base-multilingual-cased/resolve/main/vocab.txt", | |||
"bert-base-chinese": "https://huggingface.co/bert-base-chinese/resolve/main/vocab.txt", | |||
"bert-base-german-cased": "https://huggingface.co/bert-base-german-cased/resolve/main/vocab.txt", | |||
"bert-large-uncased-whole-word-masking": "https://huggingface.co/bert-large-uncased-whole-word-masking/resolve/main/vocab.txt", | |||
"bert-large-cased-whole-word-masking": "https://huggingface.co/bert-large-cased-whole-word-masking/resolve/main/vocab.txt", | |||
"bert-large-uncased-whole-word-masking-finetuned-squad": "https://huggingface.co/bert-large-uncased-whole-word-masking-finetuned-squad/resolve/main/vocab.txt", | |||
"bert-large-cased-whole-word-masking-finetuned-squad": "https://huggingface.co/bert-large-cased-whole-word-masking-finetuned-squad/resolve/main/vocab.txt", | |||
"bert-base-cased-finetuned-mrpc": "https://huggingface.co/bert-base-cased-finetuned-mrpc/resolve/main/vocab.txt", | |||
"bert-base-german-dbmdz-cased": "https://huggingface.co/bert-base-german-dbmdz-cased/resolve/main/vocab.txt", | |||
"bert-base-german-dbmdz-uncased": "https://huggingface.co/bert-base-german-dbmdz-uncased/resolve/main/vocab.txt", | |||
"TurkuNLP/bert-base-finnish-cased-v1": "https://huggingface.co/TurkuNLP/bert-base-finnish-cased-v1/resolve/main/vocab.txt", | |||
"TurkuNLP/bert-base-finnish-uncased-v1": "https://huggingface.co/TurkuNLP/bert-base-finnish-uncased-v1/resolve/main/vocab.txt", | |||
"wietsedv/bert-base-dutch-cased": "https://huggingface.co/wietsedv/bert-base-dutch-cased/resolve/main/vocab.txt", | |||
} | |||
} | |||
PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES = { | |||
"bert-base-uncased": 512, | |||
"bert-large-uncased": 512, | |||
"bert-base-cased": 512, | |||
"bert-large-cased": 512, | |||
"bert-base-multilingual-uncased": 512, | |||
"bert-base-multilingual-cased": 512, | |||
"bert-base-chinese": 512, | |||
"bert-base-german-cased": 512, | |||
"bert-large-uncased-whole-word-masking": 512, | |||
"bert-large-cased-whole-word-masking": 512, | |||
"bert-large-uncased-whole-word-masking-finetuned-squad": 512, | |||
"bert-large-cased-whole-word-masking-finetuned-squad": 512, | |||
"bert-base-cased-finetuned-mrpc": 512, | |||
"bert-base-german-dbmdz-cased": 512, | |||
"bert-base-german-dbmdz-uncased": 512, | |||
"TurkuNLP/bert-base-finnish-cased-v1": 512, | |||
"TurkuNLP/bert-base-finnish-uncased-v1": 512, | |||
"wietsedv/bert-base-dutch-cased": 512, | |||
} | |||
PRETRAINED_INIT_CONFIGURATION = { | |||
"bert-base-uncased": {"do_lower_case": True}, | |||
"bert-large-uncased": {"do_lower_case": True}, | |||
"bert-base-cased": {"do_lower_case": False}, | |||
"bert-large-cased": {"do_lower_case": False}, | |||
"bert-base-multilingual-uncased": {"do_lower_case": True}, | |||
"bert-base-multilingual-cased": {"do_lower_case": False}, | |||
"bert-base-chinese": {"do_lower_case": False}, | |||
"bert-base-german-cased": {"do_lower_case": False}, | |||
"bert-large-uncased-whole-word-masking": {"do_lower_case": True}, | |||
"bert-large-cased-whole-word-masking": {"do_lower_case": False}, | |||
"bert-large-uncased-whole-word-masking-finetuned-squad": {"do_lower_case": True}, | |||
"bert-large-cased-whole-word-masking-finetuned-squad": {"do_lower_case": False}, | |||
"bert-base-cased-finetuned-mrpc": {"do_lower_case": False}, | |||
"bert-base-german-dbmdz-cased": {"do_lower_case": False}, | |||
"bert-base-german-dbmdz-uncased": {"do_lower_case": True}, | |||
"TurkuNLP/bert-base-finnish-cased-v1": {"do_lower_case": False}, | |||
"TurkuNLP/bert-base-finnish-uncased-v1": {"do_lower_case": True}, | |||
"wietsedv/bert-base-dutch-cased": {"do_lower_case": False}, | |||
} | |||
def load_vocab(vocab_file): | |||
"""Loads a vocabulary file into a dictionary.""" | |||
vocab = collections.OrderedDict() | |||
with open(vocab_file, "r", encoding="utf-8") as reader: | |||
tokens = reader.readlines() | |||
for index, token in enumerate(tokens): | |||
token = token.rstrip("\n") | |||
vocab[token] = index | |||
return vocab | |||
def whitespace_tokenize(text): | |||
"""Runs basic whitespace cleaning and splitting on a piece of text.""" | |||
text = text.strip() | |||
if not text: | |||
return [] | |||
tokens = text.split() | |||
return tokens | |||
class BertTokenizer(PreTrainedTokenizer): | |||
r""" | |||
Construct a BERT tokenizer. Based on WordPiece. | |||
This tokenizer inherits from :class:`~transformers.PreTrainedTokenizer` which contains most of the main methods. | |||
Users should refer to this superclass for more information regarding those methods. | |||
Args: | |||
vocab_file (:obj:`str`): | |||
File containing the vocabulary. | |||
do_lower_case (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Whether or not to lowercase the input when tokenizing. | |||
do_basic_tokenize (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Whether or not to do basic tokenization before WordPiece. | |||
never_split (:obj:`Iterable`, `optional`): | |||
Collection of tokens which will never be split during tokenization. Only has an effect when | |||
:obj:`do_basic_tokenize=True` | |||
unk_token (:obj:`str`, `optional`, defaults to :obj:`"[UNK]"`): | |||
The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this | |||
token instead. | |||
sep_token (:obj:`str`, `optional`, defaults to :obj:`"[SEP]"`): | |||
The separator token, which is used when building a sequence from multiple sequences, e.g. two sequences for | |||
sequence classification or for a text and a question for question answering. It is also used as the last | |||
token of a sequence built with special tokens. | |||
pad_token (:obj:`str`, `optional`, defaults to :obj:`"[PAD]"`): | |||
The token used for padding, for example when batching sequences of different lengths. | |||
cls_token (:obj:`str`, `optional`, defaults to :obj:`"[CLS]"`): | |||
The classifier token which is used when doing sequence classification (classification of the whole sequence | |||
instead of per-token classification). It is the first token of the sequence when built with special tokens. | |||
mask_token (:obj:`str`, `optional`, defaults to :obj:`"[MASK]"`): | |||
The token used for masking values. This is the token used when training this model with masked language | |||
modeling. This is the token which the model will try to predict. | |||
tokenize_chinese_chars (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Whether or not to tokenize Chinese characters. | |||
This should likely be deactivated for Japanese (see this `issue | |||
<https://github.com/huggingface/transformers/issues/328>`__). | |||
strip_accents: (:obj:`bool`, `optional`): | |||
Whether or not to strip all accents. If this option is not specified, then it will be determined by the | |||
value for :obj:`lowercase` (as in the original BERT). | |||
""" | |||
vocab_files_names = VOCAB_FILES_NAMES | |||
pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP | |||
pretrained_init_configuration = PRETRAINED_INIT_CONFIGURATION | |||
max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES | |||
def __init__( | |||
self, | |||
vocab_file, | |||
do_lower_case=True, | |||
do_basic_tokenize=True, | |||
never_split=None, | |||
unk_token="[UNK]", | |||
sep_token="[SEP]", | |||
pad_token="[PAD]", | |||
cls_token="[CLS]", | |||
mask_token="[MASK]", | |||
tokenize_chinese_chars=True, | |||
strip_accents=None, | |||
**kwargs | |||
): | |||
super().__init__( | |||
do_lower_case=do_lower_case, | |||
do_basic_tokenize=do_basic_tokenize, | |||
never_split=never_split, | |||
unk_token=unk_token, | |||
sep_token=sep_token, | |||
pad_token=pad_token, | |||
cls_token=cls_token, | |||
mask_token=mask_token, | |||
tokenize_chinese_chars=tokenize_chinese_chars, | |||
strip_accents=strip_accents, | |||
**kwargs, | |||
) | |||
if not os.path.isfile(vocab_file): | |||
raise ValueError( | |||
f"Can't find a vocabulary file at path '{vocab_file}'. To load the vocabulary from a Google pretrained " | |||
"model use `tokenizer = BertTokenizer.from_pretrained(PRETRAINED_MODEL_NAME)`" | |||
) | |||
self.vocab = load_vocab(vocab_file) | |||
self.ids_to_tokens = collections.OrderedDict([(ids, tok) for tok, ids in self.vocab.items()]) | |||
self.do_basic_tokenize = do_basic_tokenize | |||
if do_basic_tokenize: | |||
self.basic_tokenizer = BasicTokenizer( | |||
do_lower_case=do_lower_case, | |||
never_split=never_split, | |||
tokenize_chinese_chars=tokenize_chinese_chars, | |||
strip_accents=strip_accents, | |||
) | |||
self.wordpiece_tokenizer = WordpieceTokenizer(vocab=self.vocab, unk_token=self.unk_token) | |||
@property | |||
def do_lower_case(self): | |||
return self.basic_tokenizer.do_lower_case | |||
@property | |||
def vocab_size(self): | |||
return len(self.vocab) | |||
def get_vocab(self): | |||
return dict(self.vocab, **self.added_tokens_encoder) | |||
def _tokenize(self, text): | |||
split_tokens = [] | |||
if self.do_basic_tokenize: | |||
for token in self.basic_tokenizer.tokenize(text, never_split=self.all_special_tokens): | |||
# If the token is part of the never_split set | |||
if token in self.basic_tokenizer.never_split: | |||
split_tokens.append(token) | |||
else: | |||
split_tokens += self.wordpiece_tokenizer.tokenize(token) | |||
else: | |||
split_tokens = self.wordpiece_tokenizer.tokenize(text) | |||
return split_tokens | |||
def _convert_token_to_id(self, token): | |||
"""Converts a token (str) in an id using the vocab.""" | |||
return self.vocab.get(token, self.vocab.get(self.unk_token)) | |||
def _convert_id_to_token(self, index): | |||
"""Converts an index (integer) in a token (str) using the vocab.""" | |||
return self.ids_to_tokens.get(index, self.unk_token) | |||
def convert_tokens_to_string(self, tokens): | |||
"""Converts a sequence of tokens (string) in a single string.""" | |||
out_string = " ".join(tokens).replace(" ##", "").strip() | |||
return out_string | |||
def build_inputs_with_special_tokens( | |||
self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None | |||
) -> List[int]: | |||
""" | |||
Build model inputs from a sequence or a pair of sequence for sequence classification tasks by concatenating and | |||
adding special tokens. A BERT sequence has the following format: | |||
- single sequence: ``[CLS] X [SEP]`` | |||
- pair of sequences: ``[CLS] A [SEP] B [SEP]`` | |||
Args: | |||
token_ids_0 (:obj:`List[int]`): | |||
List of IDs to which the special tokens will be added. | |||
token_ids_1 (:obj:`List[int]`, `optional`): | |||
Optional second list of IDs for sequence pairs. | |||
Returns: | |||
:obj:`List[int]`: List of `input IDs <../glossary.html#input-ids>`__ with the appropriate special tokens. | |||
""" | |||
if token_ids_1 is None: | |||
return [self.cls_token_id] + token_ids_0 + [self.sep_token_id] | |||
cls = [self.cls_token_id] | |||
sep = [self.sep_token_id] | |||
return cls + token_ids_0 + sep + token_ids_1 + sep | |||
def get_special_tokens_mask( | |||
self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None, already_has_special_tokens: bool = False | |||
) -> List[int]: | |||
""" | |||
Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding | |||
special tokens using the tokenizer ``prepare_for_model`` method. | |||
Args: | |||
token_ids_0 (:obj:`List[int]`): | |||
List of IDs. | |||
token_ids_1 (:obj:`List[int]`, `optional`): | |||
Optional second list of IDs for sequence pairs. | |||
already_has_special_tokens (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not the token list is already formatted with special tokens for the model. | |||
Returns: | |||
:obj:`List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token. | |||
""" | |||
if already_has_special_tokens: | |||
return super().get_special_tokens_mask( | |||
token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True | |||
) | |||
if token_ids_1 is not None: | |||
return [1] + ([0] * len(token_ids_0)) + [1] + ([0] * len(token_ids_1)) + [1] | |||
return [1] + ([0] * len(token_ids_0)) + [1] | |||
def create_token_type_ids_from_sequences( | |||
self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None | |||
) -> List[int]: | |||
""" | |||
Create a mask from the two sequences passed to be used in a sequence-pair classification task. A BERT sequence | |||
pair mask has the following format: | |||
:: | |||
0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 | |||
| first sequence | second sequence | | |||
If :obj:`token_ids_1` is :obj:`None`, this method only returns the first portion of the mask (0s). | |||
Args: | |||
token_ids_0 (:obj:`List[int]`): | |||
List of IDs. | |||
token_ids_1 (:obj:`List[int]`, `optional`): | |||
Optional second list of IDs for sequence pairs. | |||
Returns: | |||
:obj:`List[int]`: List of `token type IDs <../glossary.html#token-type-ids>`_ according to the given | |||
sequence(s). | |||
""" | |||
sep = [self.sep_token_id] | |||
cls = [self.cls_token_id] | |||
if token_ids_1 is None: | |||
return len(cls + token_ids_0 + sep) * [0] | |||
return len(cls + token_ids_0 + sep) * [0] + len(token_ids_1 + sep) * [1] | |||
def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None) -> Tuple[str]: | |||
index = 0 | |||
if os.path.isdir(save_directory): | |||
vocab_file = os.path.join( | |||
save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"] | |||
) | |||
else: | |||
vocab_file = (filename_prefix + "-" if filename_prefix else "") + save_directory | |||
with open(vocab_file, "w", encoding="utf-8") as writer: | |||
for token, token_index in sorted(self.vocab.items(), key=lambda kv: kv[1]): | |||
if index != token_index: | |||
logger.warning( | |||
f"Saving vocabulary to {vocab_file}: vocabulary indices are not consecutive." | |||
" Please check that the vocabulary is not corrupted!" | |||
) | |||
index = token_index | |||
writer.write(token + "\n") | |||
index += 1 | |||
return (vocab_file,) | |||
class BasicTokenizer(object): | |||
""" | |||
Constructs a BasicTokenizer that will run basic tokenization (punctuation splitting, lower casing, etc.). | |||
Args: | |||
do_lower_case (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Whether or not to lowercase the input when tokenizing. | |||
never_split (:obj:`Iterable`, `optional`): | |||
Collection of tokens which will never be split during tokenization. Only has an effect when | |||
:obj:`do_basic_tokenize=True` | |||
tokenize_chinese_chars (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Whether or not to tokenize Chinese characters. | |||
This should likely be deactivated for Japanese (see this `issue | |||
<https://github.com/huggingface/transformers/issues/328>`__). | |||
strip_accents: (:obj:`bool`, `optional`): | |||
Whether or not to strip all accents. If this option is not specified, then it will be determined by the | |||
value for :obj:`lowercase` (as in the original BERT). | |||
""" | |||
def __init__(self, do_lower_case=True, never_split=None, tokenize_chinese_chars=True, strip_accents=None): | |||
if never_split is None: | |||
never_split = [] | |||
self.do_lower_case = do_lower_case | |||
self.never_split = set(never_split) | |||
self.tokenize_chinese_chars = tokenize_chinese_chars | |||
self.strip_accents = strip_accents | |||
def tokenize(self, text, never_split=None): | |||
""" | |||
Basic Tokenization of a piece of text. Split on "white spaces" only, for sub-word tokenization, see | |||
WordPieceTokenizer. | |||
Args: | |||
**never_split**: (`optional`) list of str | |||
Kept for backward compatibility purposes. Now implemented directly at the base class level (see | |||
:func:`PreTrainedTokenizer.tokenize`) List of token not to split. | |||
""" | |||
# union() returns a new set by concatenating the two sets. | |||
never_split = self.never_split.union(set(never_split)) if never_split else self.never_split | |||
text = self._clean_text(text) | |||
# This was added on November 1st, 2018 for the multilingual and Chinese | |||
# models. This is also applied to the English models now, but it doesn't | |||
# matter since the English models were not trained on any Chinese data | |||
# and generally don't have any Chinese data in them (there are Chinese | |||
# characters in the vocabulary because Wikipedia does have some Chinese | |||
# words in the English Wikipedia.). | |||
if self.tokenize_chinese_chars: | |||
text = self._tokenize_chinese_chars(text) | |||
orig_tokens = whitespace_tokenize(text) | |||
split_tokens = [] | |||
for token in orig_tokens: | |||
if token not in never_split: | |||
if self.do_lower_case: | |||
token = token.lower() | |||
if self.strip_accents is not False: | |||
token = self._run_strip_accents(token) | |||
elif self.strip_accents: | |||
token = self._run_strip_accents(token) | |||
split_tokens.extend(self._run_split_on_punc(token, never_split)) | |||
output_tokens = whitespace_tokenize(" ".join(split_tokens)) | |||
return output_tokens | |||
def _run_strip_accents(self, text): | |||
"""Strips accents from a piece of text.""" | |||
text = unicodedata.normalize("NFD", text) | |||
output = [] | |||
for char in text: | |||
cat = unicodedata.category(char) | |||
if cat == "Mn": | |||
continue | |||
output.append(char) | |||
return "".join(output) | |||
def _run_split_on_punc(self, text, never_split=None): | |||
"""Splits punctuation on a piece of text.""" | |||
if never_split is not None and text in never_split: | |||
return [text] | |||
chars = list(text) | |||
i = 0 | |||
start_new_word = True | |||
output = [] | |||
while i < len(chars): | |||
char = chars[i] | |||
if _is_punctuation(char): | |||
output.append([char]) | |||
start_new_word = True | |||
else: | |||
if start_new_word: | |||
output.append([]) | |||
start_new_word = False | |||
output[-1].append(char) | |||
i += 1 | |||
return ["".join(x) for x in output] | |||
def _tokenize_chinese_chars(self, text): | |||
"""Adds whitespace around any CJK character.""" | |||
output = [] | |||
for char in text: | |||
cp = ord(char) | |||
if self._is_chinese_char(cp): | |||
output.append(" ") | |||
output.append(char) | |||
output.append(" ") | |||
else: | |||
output.append(char) | |||
return "".join(output) | |||
def _is_chinese_char(self, cp): | |||
"""Checks whether CP is the codepoint of a CJK character.""" | |||
# This defines a "chinese character" as anything in the CJK Unicode block: | |||
# https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) | |||
# | |||
# Note that the CJK Unicode block is NOT all Japanese and Korean characters, | |||
# despite its name. The modern Korean Hangul alphabet is a different block, | |||
# as is Japanese Hiragana and Katakana. Those alphabets are used to write | |||
# space-separated words, so they are not treated specially and handled | |||
# like the all of the other languages. | |||
if ( | |||
(cp >= 0x4E00 and cp <= 0x9FFF) | |||
or (cp >= 0x3400 and cp <= 0x4DBF) # | |||
or (cp >= 0x20000 and cp <= 0x2A6DF) # | |||
or (cp >= 0x2A700 and cp <= 0x2B73F) # | |||
or (cp >= 0x2B740 and cp <= 0x2B81F) # | |||
or (cp >= 0x2B820 and cp <= 0x2CEAF) # | |||
or (cp >= 0xF900 and cp <= 0xFAFF) | |||
or (cp >= 0x2F800 and cp <= 0x2FA1F) # | |||
): # | |||
return True | |||
return False | |||
def _clean_text(self, text): | |||
"""Performs invalid character removal and whitespace cleanup on text.""" | |||
output = [] | |||
for char in text: | |||
cp = ord(char) | |||
if cp == 0 or cp == 0xFFFD or _is_control(char): | |||
continue | |||
if _is_whitespace(char): | |||
output.append(" ") | |||
else: | |||
output.append(char) | |||
return "".join(output) | |||
class WordpieceTokenizer(object): | |||
"""Runs WordPiece tokenization.""" | |||
def __init__(self, vocab, unk_token, max_input_chars_per_word=100): | |||
self.vocab = vocab | |||
self.unk_token = unk_token | |||
self.max_input_chars_per_word = max_input_chars_per_word | |||
def tokenize(self, text): | |||
""" | |||
Tokenizes a piece of text into its word pieces. This uses a greedy longest-match-first algorithm to perform | |||
tokenization using the given vocabulary. | |||
For example, :obj:`input = "unaffable"` wil return as output :obj:`["un", "##aff", "##able"]`. | |||
Args: | |||
text: A single token or whitespace separated tokens. This should have | |||
already been passed through `BasicTokenizer`. | |||
Returns: | |||
A list of wordpiece tokens. | |||
""" | |||
output_tokens = [] | |||
for token in whitespace_tokenize(text): | |||
chars = list(token) | |||
if len(chars) > self.max_input_chars_per_word: | |||
output_tokens.append(self.unk_token) | |||
continue | |||
is_bad = False | |||
start = 0 | |||
sub_tokens = [] | |||
while start < len(chars): | |||
end = len(chars) | |||
cur_substr = None | |||
while start < end: | |||
substr = "".join(chars[start:end]) | |||
if start > 0: | |||
substr = "##" + substr | |||
if substr in self.vocab: | |||
cur_substr = substr | |||
break | |||
end -= 1 | |||
if cur_substr is None: | |||
is_bad = True | |||
break | |||
sub_tokens.append(cur_substr) | |||
start = end | |||
if is_bad: | |||
output_tokens.append(self.unk_token) | |||
else: | |||
output_tokens.extend(sub_tokens) | |||
return output_tokens |
@@ -0,0 +1,12 @@ | |||
__all__ = [ | |||
"CPT_PRETRAINED_MODEL_ARCHIVE_LIST", | |||
"CPTForConditionalGeneration", | |||
"CPTForSequenceClassification", | |||
"CPTForMaskedLM", | |||
"CPTForQuestionAnswering", | |||
"CPTModel", | |||
"CPTPretrainedModel", | |||
] | |||
from .modeling_cpt import CPT_PRETRAINED_MODEL_ARCHIVE_LIST, CPTForConditionalGeneration, CPTForSequenceClassification, \ | |||
CPTForMaskedLM, CPTForQuestionAnswering, CPTModel, CPTPretrainedModel |
@@ -0,0 +1,19 @@ | |||
__all__ = [ | |||
"GPT2_PRETRAINED_CONFIG_ARCHIVE_MAP", | |||
"GPT2Config", | |||
"GPT2_PRETRAINED_MODEL_ARCHIVE_LIST", | |||
"GPT2DoubleHeadsModel", | |||
"GPT2ForSequenceClassification", | |||
"GPT2ForTokenClassification", | |||
"GPT2LMHeadModel", | |||
"GPT2Model", | |||
"GPT2PreTrainedModel", | |||
"GPT2Tokenizer", | |||
] | |||
from .configuration_gpt2 import GPT2_PRETRAINED_CONFIG_ARCHIVE_MAP, GPT2Config | |||
from .tokenization_gpt2 import GPT2Tokenizer | |||
from .modeling_gpt2 import GPT2_PRETRAINED_MODEL_ARCHIVE_LIST, GPT2DoubleHeadsModel, GPT2ForSequenceClassification, \ | |||
GPT2ForTokenClassification, GPT2LMHeadModel, GPT2Model, GPT2PreTrainedModel |
@@ -0,0 +1,184 @@ | |||
# coding=utf-8 | |||
# Copyright 2018 The OpenAI Team Authors and 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. | |||
""" OpenAI GPT-2 configuration """ | |||
from fastNLP.transformers.torch.configuration_utils import PretrainedConfig | |||
__all__ = [ | |||
"GPT2_PRETRAINED_CONFIG_ARCHIVE_MAP", | |||
"GPT2Config", | |||
] | |||
GPT2_PRETRAINED_CONFIG_ARCHIVE_MAP = { | |||
"gpt2": "https://huggingface.co/gpt2/resolve/main/config.json", | |||
"gpt2-medium": "https://huggingface.co/gpt2-medium/resolve/main/config.json", | |||
"gpt2-large": "https://huggingface.co/gpt2-large/resolve/main/config.json", | |||
"gpt2-xl": "https://huggingface.co/gpt2-xl/resolve/main/config.json", | |||
"distilgpt2": "https://huggingface.co/distilgpt2/resolve/main/config.json", | |||
} | |||
class GPT2Config(PretrainedConfig): | |||
""" | |||
This is the configuration class to store the configuration of a :class:`~transformers.GPT2Model` or a | |||
:class:`~transformers.TFGPT2Model`. It is used to instantiate a GPT-2 model according to the specified arguments, | |||
defining the model architecture. Instantiating a configuration with the defaults will yield a similar configuration | |||
to that of the GPT-2 `small <https://huggingface.co/gpt2>`__ 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 50257): | |||
Vocabulary size of the GPT-2 model. Defines the number of different tokens that can be represented by the | |||
:obj:`inputs_ids` passed when calling :class:`~transformers.GPT2Model` or | |||
:class:`~transformers.TFGPT2Model`. | |||
n_positions (:obj:`int`, `optional`, defaults to 1024): | |||
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_ctx (:obj:`int`, `optional`, defaults to 1024): | |||
Dimensionality of the causal mask (usually same as n_positions). | |||
n_embd (:obj:`int`, `optional`, defaults to 768): | |||
Dimensionality of the embeddings and hidden states. | |||
n_layer (:obj:`int`, `optional`, defaults to 12): | |||
Number of hidden layers in the Transformer encoder. | |||
n_head (:obj:`int`, `optional`, defaults to 12): | |||
Number of attention heads for each attention layer in the Transformer encoder. | |||
n_inner (:obj:`int`, `optional`, defaults to None): | |||
Dimensionality of the inner feed-forward layers. :obj:`None` will set it to 4 times n_embd | |||
activation_function (:obj:`str`, `optional`, defaults to :obj:`"gelu"`): | |||
Activation function, to be selected in the list :obj:`["relu", "silu", "gelu", "tanh", "gelu_new"]`. | |||
resid_pdrop (:obj:`float`, `optional`, defaults to 0.1): | |||
The dropout probability for all fully connected layers in the embeddings, encoder, and pooler. | |||
embd_pdrop (:obj:`int`, `optional`, defaults to 0.1): | |||
The dropout ratio for the embeddings. | |||
attn_pdrop (:obj:`float`, `optional`, defaults to 0.1): | |||
The dropout ratio for the attention. | |||
layer_norm_epsilon (:obj:`float`, `optional`, defaults to 1e-5): | |||
The epsilon to use in the layer normalization layers | |||
initializer_range (:obj:`float`, `optional`, defaults to 0.02): | |||
The standard deviation of the truncated_normal_initializer for initializing all weight matrices. | |||
summary_type (:obj:`string`, `optional`, defaults to :obj:`"cls_index"`): | |||
Argument used when doing sequence summary, used in the models :class:`~transformers.GPT2DoubleHeadsModel` | |||
and :class:`~transformers.TFGPT2DoubleHeadsModel`. | |||
Has to be one of the following options: | |||
- :obj:`"last"`: Take the last token hidden state (like XLNet). | |||
- :obj:`"first"`: Take the first token hidden state (like BERT). | |||
- :obj:`"mean"`: Take the mean of all tokens hidden states. | |||
- :obj:`"cls_index"`: Supply a Tensor of classification token position (like GPT/GPT-2). | |||
- :obj:`"attn"`: Not implemented now, use multi-head attention. | |||
summary_use_proj (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Argument used when doing sequence summary, used in the models :class:`~transformers.GPT2DoubleHeadsModel` | |||
and :class:`~transformers.TFGPT2DoubleHeadsModel`. | |||
Whether or not to add a projection after the vector extraction. | |||
summary_activation (:obj:`str`, `optional`): | |||
Argument used when doing sequence summary. Used in for the multiple choice head in | |||
:class:`~transformers.GPT2DoubleHeadsModel`. | |||
Pass :obj:`"tanh"` for a tanh activation to the output, any other value will result in no activation. | |||
summary_proj_to_labels (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Argument used when doing sequence summary, used in the models :class:`~transformers.GPT2DoubleHeadsModel` | |||
and :class:`~transformers.TFGPT2DoubleHeadsModel`. | |||
Whether the projection outputs should have :obj:`config.num_labels` or :obj:`config.hidden_size` classes. | |||
summary_first_dropout (:obj:`float`, `optional`, defaults to 0.1): | |||
Argument used when doing sequence summary, used in the models :class:`~transformers.GPT2DoubleHeadsModel` | |||
and :class:`~transformers.TFGPT2DoubleHeadsModel`. | |||
The dropout ratio to be used after the projection and activation. | |||
scale_attn_weights (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Scale attention weights by dividing by sqrt(hidden_size).. | |||
use_cache (:obj:`bool`, `optional`, defaults to :obj:`True`): | |||
Whether or not the model should return the last key/values attentions (not used by all models). | |||
Example:: | |||
>>> from transformers import GPT2Model, GPT2Config | |||
>>> # Initializing a GPT2 configuration | |||
>>> configuration = GPT2Config() | |||
>>> # Initializing a model from the configuration | |||
>>> model = GPT2Model(configuration) | |||
>>> # Accessing the model configuration | |||
>>> configuration = model.config | |||
""" | |||
model_type = "gpt2" | |||
keys_to_ignore_at_inference = ["past_key_values"] | |||
attribute_map = { | |||
"hidden_size": "n_embd", | |||
"max_position_embeddings": "n_positions", | |||
"num_attention_heads": "n_head", | |||
"num_hidden_layers": "n_layer", | |||
} | |||
def __init__( | |||
self, | |||
vocab_size=50257, | |||
n_positions=1024, | |||
n_ctx=1024, | |||
n_embd=768, | |||
n_layer=12, | |||
n_head=12, | |||
n_inner=None, | |||
activation_function="gelu_new", | |||
resid_pdrop=0.1, | |||
embd_pdrop=0.1, | |||
attn_pdrop=0.1, | |||
layer_norm_epsilon=1e-5, | |||
initializer_range=0.02, | |||
summary_type="cls_index", | |||
summary_use_proj=True, | |||
summary_activation=None, | |||
summary_proj_to_labels=True, | |||
summary_first_dropout=0.1, | |||
scale_attn_weights=True, | |||
use_cache=True, | |||
bos_token_id=50256, | |||
eos_token_id=50256, | |||
**kwargs | |||
): | |||
self.vocab_size = vocab_size | |||
self.n_ctx = n_ctx | |||
self.n_positions = n_positions | |||
self.n_embd = n_embd | |||
self.n_layer = n_layer | |||
self.n_head = n_head | |||
self.n_inner = n_inner | |||
self.activation_function = activation_function | |||
self.resid_pdrop = resid_pdrop | |||
self.embd_pdrop = embd_pdrop | |||
self.attn_pdrop = attn_pdrop | |||
self.layer_norm_epsilon = layer_norm_epsilon | |||
self.initializer_range = initializer_range | |||
self.summary_type = summary_type | |||
self.summary_use_proj = summary_use_proj | |||
self.summary_activation = summary_activation | |||
self.summary_first_dropout = summary_first_dropout | |||
self.summary_proj_to_labels = summary_proj_to_labels | |||
self.scale_attn_weights = scale_attn_weights | |||
self.use_cache = use_cache | |||
self.bos_token_id = bos_token_id | |||
self.eos_token_id = eos_token_id | |||
super().__init__(bos_token_id=bos_token_id, eos_token_id=eos_token_id, **kwargs) |
@@ -0,0 +1,308 @@ | |||
# coding=utf-8 | |||
# Copyright 2018 The Open AI Team Authors and The HuggingFace Inc. team. | |||
# | |||
# 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. | |||
"""Tokenization classes for OpenAI GPT.""" | |||
import json | |||
import os | |||
from functools import lru_cache | |||
from typing import TYPE_CHECKING, List, Optional, Tuple | |||
import regex as re | |||
from fastNLP.transformers.torch.tokenization_utils import AddedToken, PreTrainedTokenizer | |||
# if TYPE_CHECKING: | |||
# from transformers.pipelines.conversational import Conversation | |||
from fastNLP.core.log import logger | |||
__all__ = [ | |||
"GPT2Tokenizer", | |||
] | |||
VOCAB_FILES_NAMES = { | |||
"vocab_file": "vocab.json", | |||
"merges_file": "merges.txt", | |||
} | |||
PRETRAINED_VOCAB_FILES_MAP = { | |||
"vocab_file": { | |||
"gpt2": "https://huggingface.co/gpt2/resolve/main/vocab.json", | |||
"gpt2-medium": "https://huggingface.co/gpt2-medium/resolve/main/vocab.json", | |||
"gpt2-large": "https://huggingface.co/gpt2-large/resolve/main/vocab.json", | |||
"gpt2-xl": "https://huggingface.co/gpt2-xl/resolve/main/vocab.json", | |||
"distilgpt2": "https://huggingface.co/distilgpt2/resolve/main/vocab.json", | |||
}, | |||
"merges_file": { | |||
"gpt2": "https://huggingface.co/gpt2/resolve/main/merges.txt", | |||
"gpt2-medium": "https://huggingface.co/gpt2-medium/resolve/main/merges.txt", | |||
"gpt2-large": "https://huggingface.co/gpt2-large/resolve/main/merges.txt", | |||
"gpt2-xl": "https://huggingface.co/gpt2-xl/resolve/main/merges.txt", | |||
"distilgpt2": "https://huggingface.co/distilgpt2/resolve/main/merges.txt", | |||
}, | |||
} | |||
PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES = { | |||
"gpt2": 1024, | |||
"gpt2-medium": 1024, | |||
"gpt2-large": 1024, | |||
"gpt2-xl": 1024, | |||
"distilgpt2": 1024, | |||
} | |||
@lru_cache() | |||
def bytes_to_unicode(): | |||
""" | |||
Returns list of utf-8 byte and a mapping to unicode strings. We specifically avoids mapping to whitespace/control | |||
characters the bpe code barfs on. | |||
The reversible bpe codes work on unicode strings. This means you need a large # of unicode characters in your vocab | |||
if you want to avoid UNKs. When you're at something like a 10B token dataset you end up needing around 5K for | |||
decent coverage. This is a significant percentage of your normal, say, 32K bpe vocab. To avoid that, we want lookup | |||
tables between utf-8 bytes and unicode strings. | |||
""" | |||
bs = ( | |||
list(range(ord("!"), ord("~") + 1)) + list(range(ord("¡"), ord("¬") + 1)) + list(range(ord("®"), ord("ÿ") + 1)) | |||
) | |||
cs = bs[:] | |||
n = 0 | |||
for b in range(2 ** 8): | |||
if b not in bs: | |||
bs.append(b) | |||
cs.append(2 ** 8 + n) | |||
n += 1 | |||
cs = [chr(n) for n in cs] | |||
return dict(zip(bs, cs)) | |||
def get_pairs(word): | |||
""" | |||
Return set of symbol pairs in a word. | |||
Word is represented as tuple of symbols (symbols being variable-length strings). | |||
""" | |||
pairs = set() | |||
prev_char = word[0] | |||
for char in word[1:]: | |||
pairs.add((prev_char, char)) | |||
prev_char = char | |||
return pairs | |||
class GPT2Tokenizer(PreTrainedTokenizer): | |||
""" | |||
Construct a GPT-2 tokenizer. Based on byte-level Byte-Pair-Encoding. | |||
This tokenizer has been trained to treat spaces like parts of the tokens (a bit like sentencepiece) so a word will | |||
be encoded differently whether it is at the beginning of the sentence (without space) or not: | |||
:: | |||
>>> from transformers import GPT2Tokenizer | |||
>>> tokenizer = GPT2Tokenizer.from_pretrained("gpt2") | |||
>>> tokenizer("Hello world")['input_ids'] | |||
[15496, 995] | |||
>>> tokenizer(" Hello world")['input_ids'] | |||
[18435, 995] | |||
You can get around that behavior by passing ``add_prefix_space=True`` when instantiating this tokenizer or when you | |||
call it on some text, but since the model was not pretrained this way, it might yield a decrease in performance. | |||
.. note:: | |||
When used with ``is_split_into_words=True``, this tokenizer will add a space before each word (even the first | |||
one). | |||
This tokenizer inherits from :class:`~transformers.PreTrainedTokenizer` which contains most of the main methods. | |||
Users should refer to this superclass for more information regarding those methods. | |||
Args: | |||
vocab_file (:obj:`str`): | |||
Path to the vocabulary file. | |||
merges_file (:obj:`str`): | |||
Path to the merges file. | |||
errors (:obj:`str`, `optional`, defaults to :obj:`"replace"`): | |||
Paradigm to follow when decoding bytes to UTF-8. See `bytes.decode | |||
<https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__ for more information. | |||
unk_token (:obj:`str`, `optional`, defaults to :obj:`<|endoftext|>`): | |||
The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this | |||
token instead. | |||
bos_token (:obj:`str`, `optional`, defaults to :obj:`<|endoftext|>`): | |||
The beginning of sequence token. | |||
eos_token (:obj:`str`, `optional`, defaults to :obj:`<|endoftext|>`): | |||
The end of sequence token. | |||
add_prefix_space (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not to add an initial space to the input. This allows to treat the leading word just as any | |||
other word. (GPT2 tokenizer detect beginning of words by the preceding space). | |||
""" | |||
vocab_files_names = VOCAB_FILES_NAMES | |||
pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP | |||
max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES | |||
model_input_names = ["input_ids", "attention_mask"] | |||
def __init__( | |||
self, | |||
vocab_file, | |||
merges_file, | |||
errors="replace", | |||
unk_token="<|endoftext|>", | |||
bos_token="<|endoftext|>", | |||
eos_token="<|endoftext|>", | |||
add_prefix_space=False, | |||
**kwargs | |||
): | |||
bos_token = AddedToken(bos_token, lstrip=False, rstrip=False) if isinstance(bos_token, str) else bos_token | |||
eos_token = AddedToken(eos_token, lstrip=False, rstrip=False) if isinstance(eos_token, str) else eos_token | |||
unk_token = AddedToken(unk_token, lstrip=False, rstrip=False) if isinstance(unk_token, str) else unk_token | |||
super().__init__( | |||
errors=errors, | |||
unk_token=unk_token, | |||
bos_token=bos_token, | |||
eos_token=eos_token, | |||
add_prefix_space=add_prefix_space, | |||
**kwargs, | |||
) | |||
with open(vocab_file, encoding="utf-8") as vocab_handle: | |||
self.encoder = json.load(vocab_handle) | |||
self.decoder = {v: k for k, v in self.encoder.items()} | |||
self.errors = errors # how to handle errors in decoding | |||
self.byte_encoder = bytes_to_unicode() | |||
self.byte_decoder = {v: k for k, v in self.byte_encoder.items()} | |||
with open(merges_file, encoding="utf-8") as merges_handle: | |||
bpe_merges = merges_handle.read().split("\n")[1:-1] | |||
bpe_merges = [tuple(merge.split()) for merge in bpe_merges] | |||
self.bpe_ranks = dict(zip(bpe_merges, range(len(bpe_merges)))) | |||
self.cache = {} | |||
self.add_prefix_space = add_prefix_space | |||
# Should have added re.IGNORECASE so BPE merges can happen for capitalized versions of contractions | |||
self.pat = re.compile(r"""'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+""") | |||
@property | |||
def vocab_size(self): | |||
return len(self.encoder) | |||
def get_vocab(self): | |||
return dict(self.encoder, **self.added_tokens_encoder) | |||
def bpe(self, token): | |||
if token in self.cache: | |||
return self.cache[token] | |||
word = tuple(token) | |||
pairs = get_pairs(word) | |||
if not pairs: | |||
return token | |||
while True: | |||
bigram = min(pairs, key=lambda pair: self.bpe_ranks.get(pair, float("inf"))) | |||
if bigram not in self.bpe_ranks: | |||
break | |||
first, second = bigram | |||
new_word = [] | |||
i = 0 | |||
while i < len(word): | |||
try: | |||
j = word.index(first, i) | |||
except ValueError: | |||
new_word.extend(word[i:]) | |||
break | |||
else: | |||
new_word.extend(word[i:j]) | |||
i = j | |||
if word[i] == first and i < len(word) - 1 and word[i + 1] == second: | |||
new_word.append(first + second) | |||
i += 2 | |||
else: | |||
new_word.append(word[i]) | |||
i += 1 | |||
new_word = tuple(new_word) | |||
word = new_word | |||
if len(word) == 1: | |||
break | |||
else: | |||
pairs = get_pairs(word) | |||
word = " ".join(word) | |||
self.cache[token] = word | |||
return word | |||
def _tokenize(self, text): | |||
"""Tokenize a string.""" | |||
bpe_tokens = [] | |||
for token in re.findall(self.pat, text): | |||
token = "".join( | |||
self.byte_encoder[b] for b in token.encode("utf-8") | |||
) # Maps all our bytes to unicode strings, avoiding control tokens of the BPE (spaces in our case) | |||
bpe_tokens.extend(bpe_token for bpe_token in self.bpe(token).split(" ")) | |||
return bpe_tokens | |||
def _convert_token_to_id(self, token): | |||
"""Converts a token (str) in an id using the vocab.""" | |||
return self.encoder.get(token, self.encoder.get(self.unk_token)) | |||
def _convert_id_to_token(self, index): | |||
"""Converts an index (integer) in a token (str) using the vocab.""" | |||
return self.decoder.get(index) | |||
def convert_tokens_to_string(self, tokens): | |||
"""Converts a sequence of tokens (string) in a single string.""" | |||
text = "".join(tokens) | |||
text = bytearray([self.byte_decoder[c] for c in text]).decode("utf-8", errors=self.errors) | |||
return text | |||
def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None) -> Tuple[str]: | |||
if not os.path.isdir(save_directory): | |||
logger.error(f"Vocabulary path ({save_directory}) should be a directory") | |||
return | |||
vocab_file = os.path.join( | |||
save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"] | |||
) | |||
merge_file = os.path.join( | |||
save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["merges_file"] | |||
) | |||
with open(vocab_file, "w", encoding="utf-8") as f: | |||
f.write(json.dumps(self.encoder, ensure_ascii=False)) | |||
index = 0 | |||
with open(merge_file, "w", encoding="utf-8") as writer: | |||
writer.write("#version: 0.2\n") | |||
for bpe_tokens, token_index in sorted(self.bpe_ranks.items(), key=lambda kv: kv[1]): | |||
if index != token_index: | |||
logger.warning( | |||
f"Saving vocabulary to {merge_file}: BPE merge indices are not consecutive." | |||
" Please check that the tokenizer is not corrupted!" | |||
) | |||
index = token_index | |||
writer.write(" ".join(bpe_tokens) + "\n") | |||
index += 1 | |||
return vocab_file, merge_file | |||
def prepare_for_tokenization(self, text, is_split_into_words=False, **kwargs): | |||
add_prefix_space = kwargs.pop("add_prefix_space", self.add_prefix_space) | |||
if is_split_into_words or add_prefix_space: | |||
text = " " + text | |||
return (text, kwargs) | |||
# def _build_conversation_input_ids(self, conversation: "Conversation") -> List[int]: | |||
# input_ids = [] | |||
# for is_user, text in conversation.iter_texts(): | |||
# input_ids.extend(self.encode(text, add_special_tokens=False) + [self.eos_token_id]) | |||
# if len(input_ids) > self.model_max_length: | |||
# input_ids = input_ids[-self.model_max_length :] | |||
# return input_ids |
@@ -0,0 +1,21 @@ | |||
__all__ = [ | |||
"ROBERTA_PRETRAINED_CONFIG_ARCHIVE_MAP", | |||
"RobertaConfig", | |||
"ROBERTA_PRETRAINED_MODEL_ARCHIVE_LIST", | |||
"RobertaForCausalLM", | |||
"RobertaForMaskedLM", | |||
"RobertaForMultipleChoice", | |||
"RobertaForQuestionAnswering", | |||
"RobertaForSequenceClassification", | |||
"RobertaForTokenClassification", | |||
"RobertaModel", | |||
"RobertaPreTrainedModel", | |||
"RobertaTokenizer", | |||
] | |||
from .configuration_roberta import ROBERTA_PRETRAINED_CONFIG_ARCHIVE_MAP, RobertaConfig | |||
from .tokenization_roberta import RobertaTokenizer | |||
from .modeling_roberta import ROBERTA_PRETRAINED_MODEL_ARCHIVE_LIST, RobertaForCausalLM, RobertaForMaskedLM, RobertaForMultipleChoice, \ | |||
RobertaForQuestionAnswering, RobertaForSequenceClassification, RobertaForTokenClassification, RobertaModel, RobertaPreTrainedModel |
@@ -0,0 +1,65 @@ | |||
# 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. | |||
""" RoBERTa configuration """ | |||
from ..bert.configuration_bert import BertConfig | |||
from fastNLP.core.log import logger | |||
__all__ = [ | |||
"ROBERTA_PRETRAINED_CONFIG_ARCHIVE_MAP", | |||
"RobertaConfig", | |||
] | |||
ROBERTA_PRETRAINED_CONFIG_ARCHIVE_MAP = { | |||
"roberta-base": "https://huggingface.co/roberta-base/resolve/main/config.json", | |||
"roberta-large": "https://huggingface.co/roberta-large/resolve/main/config.json", | |||
"roberta-large-mnli": "https://huggingface.co/roberta-large-mnli/resolve/main/config.json", | |||
"distilroberta-base": "https://huggingface.co/distilroberta-base/resolve/main/config.json", | |||
"roberta-base-openai-detector": "https://huggingface.co/roberta-base-openai-detector/resolve/main/config.json", | |||
"roberta-large-openai-detector": "https://huggingface.co/roberta-large-openai-detector/resolve/main/config.json", | |||
} | |||
class RobertaConfig(BertConfig): | |||
r""" | |||
This is the configuration class to store the configuration of a :class:`~transformers.RobertaModel` or a | |||
:class:`~transformers.TFRobertaModel`. It is used to instantiate a RoBERTa 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. | |||
The :class:`~transformers.RobertaConfig` class directly inherits :class:`~transformers.BertConfig`. It reuses the | |||
same defaults. Please check the parent class for more information. | |||
Examples:: | |||
>>> from transformers import RobertaConfig, RobertaModel | |||
>>> # Initializing a RoBERTa configuration | |||
>>> configuration = RobertaConfig() | |||
>>> # Initializing a model from the configuration | |||
>>> model = RobertaModel(configuration) | |||
>>> # Accessing the model configuration | |||
>>> configuration = model.config | |||
""" | |||
model_type = "roberta" | |||
def __init__(self, pad_token_id=1, bos_token_id=0, eos_token_id=2, **kwargs): | |||
"""Constructs RobertaConfig.""" | |||
super().__init__(pad_token_id=pad_token_id, bos_token_id=bos_token_id, eos_token_id=eos_token_id, **kwargs) |
@@ -0,0 +1,254 @@ | |||
# coding=utf-8 | |||
# Copyright 2018 The Open AI Team Authors and The HuggingFace Inc. team. | |||
# | |||
# 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. | |||
"""Tokenization classes for RoBERTa.""" | |||
from typing import List, Optional | |||
from fastNLP.transformers.torch.tokenization_utils import AddedToken | |||
from ..gpt2.tokenization_gpt2 import GPT2Tokenizer | |||
from fastNLP.core.log import logger | |||
__all__ = [ | |||
"RobertaTokenizer", | |||
] | |||
VOCAB_FILES_NAMES = { | |||
"vocab_file": "vocab.json", | |||
"merges_file": "merges.txt", | |||
} | |||
PRETRAINED_VOCAB_FILES_MAP = { | |||
"vocab_file": { | |||
"roberta-base": "https://huggingface.co/roberta-base/resolve/main/vocab.json", | |||
"roberta-large": "https://huggingface.co/roberta-large/resolve/main/vocab.json", | |||
"roberta-large-mnli": "https://huggingface.co/roberta-large-mnli/resolve/main/vocab.json", | |||
"distilroberta-base": "https://huggingface.co/distilroberta-base/resolve/main/vocab.json", | |||
"roberta-base-openai-detector": "https://huggingface.co/roberta-base-openai-detector/resolve/main/vocab.json", | |||
"roberta-large-openai-detector": "https://huggingface.co/roberta-large-openai-detector/resolve/main/vocab.json", | |||
}, | |||
"merges_file": { | |||
"roberta-base": "https://huggingface.co/roberta-base/resolve/main/merges.txt", | |||
"roberta-large": "https://huggingface.co/roberta-large/resolve/main/merges.txt", | |||
"roberta-large-mnli": "https://huggingface.co/roberta-large-mnli/resolve/main/merges.txt", | |||
"distilroberta-base": "https://huggingface.co/distilroberta-base/resolve/main/merges.txt", | |||
"roberta-base-openai-detector": "https://huggingface.co/roberta-base-openai-detector/resolve/main/merges.txt", | |||
"roberta-large-openai-detector": "https://huggingface.co/roberta-large-openai-detector/resolve/main/merges.txt", | |||
}, | |||
} | |||
PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES = { | |||
"roberta-base": 512, | |||
"roberta-large": 512, | |||
"roberta-large-mnli": 512, | |||
"distilroberta-base": 512, | |||
"roberta-base-openai-detector": 512, | |||
"roberta-large-openai-detector": 512, | |||
} | |||
class RobertaTokenizer(GPT2Tokenizer): | |||
""" | |||
Constructs a RoBERTa tokenizer, derived from the GPT-2 tokenizer, using byte-level Byte-Pair-Encoding. | |||
This tokenizer has been trained to treat spaces like parts of the tokens (a bit like sentencepiece) so a word will | |||
be encoded differently whether it is at the beginning of the sentence (without space) or not: | |||
:: | |||
>>> from transformers import RobertaTokenizer | |||
>>> tokenizer = RobertaTokenizer.from_pretrained("roberta-base") | |||
>>> tokenizer("Hello world")['input_ids'] | |||
[0, 31414, 232, 328, 2] | |||
>>> tokenizer(" Hello world")['input_ids'] | |||
[0, 20920, 232, 2] | |||
You can get around that behavior by passing ``add_prefix_space=True`` when instantiating this tokenizer or when you | |||
call it on some text, but since the model was not pretrained this way, it might yield a decrease in performance. | |||
.. note:: | |||
When used with ``is_split_into_words=True``, this tokenizer will add a space before each word (even the first | |||
one). | |||
This tokenizer inherits from :class:`~transformers.PreTrainedTokenizer` which contains most of the main methods. | |||
Users should refer to this superclass for more information regarding those methods. | |||
Args: | |||
vocab_file (:obj:`str`): | |||
Path to the vocabulary file. | |||
merges_file (:obj:`str`): | |||
Path to the merges file. | |||
errors (:obj:`str`, `optional`, defaults to :obj:`"replace"`): | |||
Paradigm to follow when decoding bytes to UTF-8. See `bytes.decode | |||
<https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__ for more information. | |||
bos_token (:obj:`str`, `optional`, defaults to :obj:`"<s>"`): | |||
The beginning of sequence token that was used during pretraining. Can be used a sequence classifier token. | |||
.. note:: | |||
When building a sequence using special tokens, this is not the token that is used for the beginning of | |||
sequence. The token used is the :obj:`cls_token`. | |||
eos_token (:obj:`str`, `optional`, defaults to :obj:`"</s>"`): | |||
The end of sequence token. | |||
.. note:: | |||
When building a sequence using special tokens, this is not the token that is used for the end of | |||
sequence. The token used is the :obj:`sep_token`. | |||
sep_token (:obj:`str`, `optional`, defaults to :obj:`"</s>"`): | |||
The separator token, which is used when building a sequence from multiple sequences, e.g. two sequences for | |||
sequence classification or for a text and a question for question answering. It is also used as the last | |||
token of a sequence built with special tokens. | |||
cls_token (:obj:`str`, `optional`, defaults to :obj:`"<s>"`): | |||
The classifier token which is used when doing sequence classification (classification of the whole sequence | |||
instead of per-token classification). It is the first token of the sequence when built with special tokens. | |||
unk_token (:obj:`str`, `optional`, defaults to :obj:`"<unk>"`): | |||
The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this | |||
token instead. | |||
pad_token (:obj:`str`, `optional`, defaults to :obj:`"<pad>"`): | |||
The token used for padding, for example when batching sequences of different lengths. | |||
mask_token (:obj:`str`, `optional`, defaults to :obj:`"<mask>"`): | |||
The token used for masking values. This is the token used when training this model with masked language | |||
modeling. This is the token which the model will try to predict. | |||
add_prefix_space (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not to add an initial space to the input. This allows to treat the leading word just as any | |||
other word. (RoBERTa tokenizer detect beginning of words by the preceding space). | |||
""" | |||
vocab_files_names = VOCAB_FILES_NAMES | |||
pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP | |||
max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES | |||
model_input_names = ["input_ids", "attention_mask"] | |||
def __init__( | |||
self, | |||
vocab_file, | |||
merges_file, | |||
errors="replace", | |||
bos_token="<s>", | |||
eos_token="</s>", | |||
sep_token="</s>", | |||
cls_token="<s>", | |||
unk_token="<unk>", | |||
pad_token="<pad>", | |||
mask_token="<mask>", | |||
add_prefix_space=False, | |||
**kwargs | |||
): | |||
bos_token = AddedToken(bos_token, lstrip=False, rstrip=False) if isinstance(bos_token, str) else bos_token | |||
eos_token = AddedToken(eos_token, lstrip=False, rstrip=False) if isinstance(eos_token, str) else eos_token | |||
sep_token = AddedToken(sep_token, lstrip=False, rstrip=False) if isinstance(sep_token, str) else sep_token | |||
cls_token = AddedToken(cls_token, lstrip=False, rstrip=False) if isinstance(cls_token, str) else cls_token | |||
unk_token = AddedToken(unk_token, lstrip=False, rstrip=False) if isinstance(unk_token, str) else unk_token | |||
pad_token = AddedToken(pad_token, lstrip=False, rstrip=False) if isinstance(pad_token, str) else pad_token | |||
# Mask token behave like a normal word, i.e. include the space before it | |||
mask_token = AddedToken(mask_token, lstrip=True, rstrip=False) if isinstance(mask_token, str) else mask_token | |||
super().__init__( | |||
vocab_file=vocab_file, | |||
merges_file=merges_file, | |||
errors=errors, | |||
bos_token=bos_token, | |||
eos_token=eos_token, | |||
unk_token=unk_token, | |||
sep_token=sep_token, | |||
cls_token=cls_token, | |||
pad_token=pad_token, | |||
mask_token=mask_token, | |||
add_prefix_space=add_prefix_space, | |||
**kwargs, | |||
) | |||
def build_inputs_with_special_tokens( | |||
self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None | |||
) -> List[int]: | |||
""" | |||
Build model inputs from a sequence or a pair of sequence for sequence classification tasks by concatenating and | |||
adding special tokens. A RoBERTa sequence has the following format: | |||
- single sequence: ``<s> X </s>`` | |||
- pair of sequences: ``<s> A </s></s> B </s>`` | |||
Args: | |||
token_ids_0 (:obj:`List[int]`): | |||
List of IDs to which the special tokens will be added. | |||
token_ids_1 (:obj:`List[int]`, `optional`): | |||
Optional second list of IDs for sequence pairs. | |||
Returns: | |||
:obj:`List[int]`: List of `input IDs <../glossary.html#input-ids>`__ with the appropriate special tokens. | |||
""" | |||
if token_ids_1 is None: | |||
return [self.cls_token_id] + token_ids_0 + [self.sep_token_id] | |||
cls = [self.cls_token_id] | |||
sep = [self.sep_token_id] | |||
return cls + token_ids_0 + sep + sep + token_ids_1 + sep | |||
def get_special_tokens_mask( | |||
self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None, already_has_special_tokens: bool = False | |||
) -> List[int]: | |||
""" | |||
Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding | |||
special tokens using the tokenizer ``prepare_for_model`` method. | |||
Args: | |||
token_ids_0 (:obj:`List[int]`): | |||
List of IDs. | |||
token_ids_1 (:obj:`List[int]`, `optional`): | |||
Optional second list of IDs for sequence pairs. | |||
already_has_special_tokens (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not the token list is already formatted with special tokens for the model. | |||
Returns: | |||
:obj:`List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token. | |||
""" | |||
if already_has_special_tokens: | |||
return super().get_special_tokens_mask( | |||
token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True | |||
) | |||
if token_ids_1 is None: | |||
return [1] + ([0] * len(token_ids_0)) + [1] | |||
return [1] + ([0] * len(token_ids_0)) + [1, 1] + ([0] * len(token_ids_1)) + [1] | |||
def create_token_type_ids_from_sequences( | |||
self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None | |||
) -> List[int]: | |||
""" | |||
Create a mask from the two sequences passed to be used in a sequence-pair classification task. RoBERTa does not | |||
make use of token type ids, therefore a list of zeros is returned. | |||
Args: | |||
token_ids_0 (:obj:`List[int]`): | |||
List of IDs. | |||
token_ids_1 (:obj:`List[int]`, `optional`): | |||
Optional second list of IDs for sequence pairs. | |||
Returns: | |||
:obj:`List[int]`: List of zeros. | |||
""" | |||
sep = [self.sep_token_id] | |||
cls = [self.cls_token_id] | |||
if token_ids_1 is None: | |||
return len(cls + token_ids_0 + sep) * [0] | |||
return len(cls + token_ids_0 + sep + sep + token_ids_1 + sep) * [0] | |||
def prepare_for_tokenization(self, text, is_split_into_words=False, **kwargs): | |||
add_prefix_space = kwargs.pop("add_prefix_space", self.add_prefix_space) | |||
if (is_split_into_words or add_prefix_space) and (len(text) > 0 and not text[0].isspace()): | |||
text = " " + text | |||
return (text, kwargs) |
@@ -0,0 +1,915 @@ | |||
# coding=utf-8 | |||
# Copyright 2020 The HuggingFace Inc. team. | |||
# | |||
# 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. | |||
""" | |||
Tokenization classes for python tokenizers. For fast tokenizers (provided by HuggingFace's tokenizers library) see | |||
tokenization_utils_fast.py | |||
""" | |||
import bisect | |||
import itertools | |||
import re | |||
import unicodedata | |||
from collections import OrderedDict | |||
from typing import Any, Dict, List, Optional, Tuple, Union, overload | |||
from .file_utils import PaddingStrategy, TensorType, add_end_docstrings | |||
from .tokenization_utils_base import ( | |||
ENCODE_KWARGS_DOCSTRING, | |||
ENCODE_PLUS_ADDITIONAL_KWARGS_DOCSTRING, | |||
INIT_TOKENIZER_DOCSTRING, | |||
AddedToken, | |||
BatchEncoding, | |||
EncodedInput, | |||
EncodedInputPair, | |||
PreTokenizedInput, | |||
PreTokenizedInputPair, | |||
PreTrainedTokenizerBase, | |||
TextInput, | |||
TextInputPair, | |||
TruncationStrategy, | |||
) | |||
from fastNLP.core.log import logger | |||
# Slow tokenizers are saved in a vocabulary plus three separated files | |||
SPECIAL_TOKENS_MAP_FILE = "special_tokens_map.json" | |||
ADDED_TOKENS_FILE = "added_tokens.json" | |||
TOKENIZER_CONFIG_FILE = "tokenizer_config.json" | |||
class Trie: | |||
""" | |||
Trie in Python. Creates a Trie out of a list of words. The trie is used to split on `added_tokens` in one pass | |||
Loose reference https://en.wikipedia.org/wiki/Trie | |||
""" | |||
def __init__(self): | |||
self.data = {} | |||
def add(self, word: str): | |||
""" | |||
Passes over every char (utf-8 char) on word and recursively adds it to the internal `data` trie representation. | |||
The special key `""` is used to represent termination. | |||
This function is idempotent, adding twice the same word will leave the trie unchanged | |||
Example:: | |||
>>> trie = Trie() | |||
>>> trie.add("Hello 友達") | |||
>>> trie.data | |||
{"H": {"e": {"l": {"l": {"o": {" ": {"友": {"達": {"": 1}}}}}}}}} | |||
>>> trie.add("Hello") | |||
>>> trie.data | |||
{"H": {"e": {"l": {"l": {"o": {"": 1, " ": {"友": {"達": {"": 1}}}}}}}}} | |||
""" | |||
if not word: | |||
# Prevent empty string | |||
return | |||
ref = self.data | |||
for char in word: | |||
ref[char] = char in ref and ref[char] or {} | |||
ref = ref[char] | |||
ref[""] = 1 | |||
def split(self, text: str) -> List[str]: | |||
""" | |||
Will look for the words added to the trie within `text`. Output is the original string splitted along the | |||
boundaries of the words found. | |||
This trie will match the longest possible word first ! | |||
Example:: | |||
>>> trie = Trie() | |||
>>> trie.split("[CLS] This is a extra_id_100") | |||
["[CLS] This is a extra_id_100"] | |||
>>> trie.add("[CLS]") | |||
>>> trie.add("extra_id_1") | |||
>>> trie.add("extra_id_100") | |||
>>> trie.split("[CLS] This is a extra_id_100") | |||
["[CLS]", " This is a ", "extra_id_100"] | |||
""" | |||
# indexes are counted left of the chars index. | |||
# "hello", index 0, is left of h, index 1 is between h and e. | |||
# index 5 is right of the "o". | |||
# States are going to capture every possible start (indexes as above) | |||
# as keys, and have as values, a pointer to the position in the trie | |||
# where we're at. This is a partial match for now. | |||
# This enables to keep track of multiple matches while we're iterating | |||
# the string | |||
# If the trie contains, "blowing", and "lower" and we encounter the | |||
# string "blower", we need to split into ["b", "lower"]. | |||
# This is where we need to keep track of multiple possible starts. | |||
states = OrderedDict() | |||
# This will contain every indices where we need | |||
# to cut. | |||
# We force to cut at offset 0 and len(text) (added later) | |||
offsets = [0] | |||
# This is used by the lookahead which needs to skip over | |||
# some text where the full match exceeded the place in the initial | |||
# for loop | |||
skip = None | |||
# Main loop, Giving this algorithm O(n) complexity | |||
for current, current_char in enumerate(text): | |||
if skip and current < skip: | |||
# Prevents the lookahead for matching twice | |||
# like extra_id_100 and id_100 | |||
continue | |||
# This will track every state | |||
# that stop matching, we need to stop tracking them. | |||
# If we look at "lowball", we're going to match "l" (add it to states), "o", "w", then | |||
# fail on "b", we need to remove 0 from the valid states. | |||
to_remove = set() | |||
# Whenever we found a match, we need to drop everything | |||
# this is a greedy algorithm, it will match on the first found token | |||
reset = False | |||
# In this case, we already have partial matches (But unfinished) | |||
for start, trie_pointer in states.items(): | |||
if "" in trie_pointer: | |||
# This is a final match, we need to reset and | |||
# store the results in `offsets`. | |||
# Lookahead to match longest first | |||
# Important in case of extra_id_1 vs extra_id_100 | |||
lookahead_index = current | |||
end = current | |||
next_char = text[lookahead_index] if lookahead_index < len(text) else None | |||
while next_char in trie_pointer: | |||
trie_pointer = trie_pointer[next_char] | |||
lookahead_index += 1 | |||
if "" in trie_pointer: | |||
end = lookahead_index | |||
skip = lookahead_index | |||
if lookahead_index == len(text): | |||
# End of string | |||
break | |||
next_char = text[lookahead_index] | |||
# End lookahead | |||
# Storing and resetting | |||
offsets.append(start) | |||
offsets.append(end) | |||
reset = True | |||
elif current_char in trie_pointer: | |||
# The current character being looked at has a match within the trie | |||
# update the pointer (it will be stored back into states later). | |||
trie_pointer = trie_pointer[current_char] | |||
# Storing back the new pointer into the states. | |||
# Partial matches got longer by one. | |||
states[start] = trie_pointer | |||
else: | |||
# The new character has not match in the trie, we need | |||
# to stop keeping track of this partial match. | |||
# We can't do it directly within the loop because of how | |||
# python iteration works | |||
to_remove.add(start) | |||
# Either clearing the full start (we found a real match) | |||
# Or clearing only the partial matches that didn't work. | |||
if reset: | |||
states = {} | |||
else: | |||
for start in to_remove: | |||
del states[start] | |||
# If this character is a starting character within the trie | |||
# start keeping track of this partial match. | |||
if current_char in self.data: | |||
states[current] = self.data[current_char] | |||
# We have a cut at the end with states. | |||
for start, trie_pointer in states.items(): | |||
if "" in trie_pointer: | |||
# This is a final match, we need to reset and | |||
# store the results in `offsets`. | |||
end = len(text) | |||
offsets.append(start) | |||
offsets.append(end) | |||
# Longest cut is always the one with lower start so the first | |||
# item so we need to break. | |||
break | |||
# We have all the offsets now, we just need to do the actual splitting. | |||
# We need to eventually add the first part of the string and the eventual | |||
# last part. | |||
offsets.append(len(text)) | |||
tokens = [] | |||
start = 0 | |||
for end in offsets: | |||
if start == end: | |||
# This might happen if there's a match at index 0 | |||
# we're also preventing zero-width cuts in case of two | |||
# consecutive matches | |||
continue | |||
tokens.append(text[start:end]) | |||
start = end | |||
return tokens | |||
def _is_whitespace(char): | |||
"""Checks whether `char` is a whitespace character.""" | |||
# \t, \n, and \r are technically control characters but we treat them | |||
# as whitespace since they are generally considered as such. | |||
if char == " " or char == "\t" or char == "\n" or char == "\r": | |||
return True | |||
cat = unicodedata.category(char) | |||
if cat == "Zs": | |||
return True | |||
return False | |||
def _is_control(char): | |||
"""Checks whether `char` is a control character.""" | |||
# These are technically control characters but we count them as whitespace | |||
# characters. | |||
if char == "\t" or char == "\n" or char == "\r": | |||
return False | |||
cat = unicodedata.category(char) | |||
if cat.startswith("C"): | |||
return True | |||
return False | |||
def _is_punctuation(char): | |||
"""Checks whether `char` is a punctuation character.""" | |||
cp = ord(char) | |||
# We treat all non-letter/number ASCII as punctuation. | |||
# Characters such as "^", "$", and "`" are not in the Unicode | |||
# Punctuation class but we treat them as punctuation anyways, for | |||
# consistency. | |||
if (cp >= 33 and cp <= 47) or (cp >= 58 and cp <= 64) or (cp >= 91 and cp <= 96) or (cp >= 123 and cp <= 126): | |||
return True | |||
cat = unicodedata.category(char) | |||
if cat.startswith("P"): | |||
return True | |||
return False | |||
def _is_end_of_word(text): | |||
"""Checks whether the last character in text is one of a punctuation, control or whitespace character.""" | |||
last_char = text[-1] | |||
return bool(_is_control(last_char) | _is_punctuation(last_char) | _is_whitespace(last_char)) | |||
def _is_start_of_word(text): | |||
"""Checks whether the first character in text is one of a punctuation, control or whitespace character.""" | |||
first_char = text[0] | |||
return bool(_is_control(first_char) | _is_punctuation(first_char) | _is_whitespace(first_char)) | |||
def _insert_one_token_to_ordered_list(token_list: List[str], new_token: str): | |||
""" | |||
Inserts one token to an ordered list if it does not already exist. Note: token_list must be sorted. | |||
""" | |||
insertion_idx = bisect.bisect_left(token_list, new_token) | |||
# Checks if new_token is already in the ordered token_list | |||
if insertion_idx < len(token_list) and token_list[insertion_idx] == new_token: | |||
# new_token is in token_list, don't add | |||
return | |||
else: | |||
token_list.insert(insertion_idx, new_token) | |||
@add_end_docstrings(INIT_TOKENIZER_DOCSTRING) | |||
class PreTrainedTokenizer(PreTrainedTokenizerBase): | |||
""" | |||
Base class for all slow tokenizers. | |||
Inherits from :class:`~transformers.tokenization_utils_base.PreTrainedTokenizerBase`. | |||
Handle all the shared methods for tokenization and special tokens as well as methods downloading/caching/loading | |||
pretrained tokenizers as well as adding tokens to the vocabulary. | |||
This class also contain the added tokens in a unified way on top of all tokenizers so we don't have to handle the | |||
specific vocabulary augmentation methods of the various underlying dictionary structures (BPE, sentencepiece...). | |||
""" | |||
def __init__(self, **kwargs): | |||
super().__init__(**kwargs) | |||
# Added tokens - We store this for both slow and fast tokenizers | |||
# until the serialization of Fast tokenizers is updated | |||
self.added_tokens_encoder: Dict[str, int] = {} | |||
self.added_tokens_decoder: Dict[int, str] = {} | |||
self.unique_no_split_tokens: List[str] = [] | |||
self.tokens_trie = Trie() | |||
self._decode_use_source_tokenizer = False | |||
@property | |||
def is_fast(self) -> bool: | |||
return False | |||
@property | |||
def vocab_size(self) -> int: | |||
""" | |||
:obj:`int`: Size of the base vocabulary (without the added tokens). | |||
""" | |||
raise NotImplementedError | |||
def get_added_vocab(self) -> Dict[str, int]: | |||
""" | |||
Returns the added tokens in the vocabulary as a dictionary of token to index. | |||
Returns: | |||
:obj:`Dict[str, int]`: The added tokens. | |||
""" | |||
return self.added_tokens_encoder | |||
def __len__(self): | |||
""" | |||
Size of the full vocabulary with the added tokens. | |||
""" | |||
return self.vocab_size + len(self.added_tokens_encoder) | |||
def _add_tokens(self, new_tokens: Union[List[str], List[AddedToken]], special_tokens: bool = False) -> int: | |||
""" | |||
Add a list of new tokens to the tokenizer class. If the new tokens are not in the vocabulary, they are added to | |||
it with indices starting from length of the current vocabulary. | |||
Args: | |||
new_tokens (:obj:`List[str]`or :obj:`List[tokenizers.AddedToken]`): | |||
Token(s) to add in vocabulary. A token is only added if it's not already in the vocabulary (tested by | |||
checking if the tokenizer assign the index of the ``unk_token`` to them). | |||
special_tokens (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not the tokens should be added as special tokens. | |||
Returns: | |||
:obj:`int`: The number of tokens actually added to the vocabulary. | |||
Examples:: | |||
# Let's see how to increase the vocabulary of Bert model and tokenizer | |||
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') | |||
model = BertModel.from_pretrained('bert-base-uncased') | |||
num_added_toks = tokenizer.add_tokens(['new_tok1', 'my_new-tok2']) | |||
print('We have added', num_added_toks, 'tokens') | |||
# Note: resize_token_embeddings expects to receive the full size of the new vocabulary, i.e. the length of the tokenizer. | |||
model.resize_token_embeddings(len(tokenizer)) | |||
""" | |||
new_tokens = [str(tok) for tok in new_tokens] | |||
tokens_to_add = [] | |||
for token in new_tokens: | |||
if not isinstance(token, str): | |||
raise TypeError(f"Token {token} is not a string but a {type(token)}.") | |||
if not special_tokens and hasattr(self, "do_lower_case") and self.do_lower_case: | |||
token = token.lower() | |||
if ( | |||
token != self.unk_token | |||
and self.convert_tokens_to_ids(token) == self.convert_tokens_to_ids(self.unk_token) | |||
and token not in tokens_to_add | |||
): | |||
tokens_to_add.append(token) | |||
if self.verbose: | |||
logger.info(f"Adding {token} to the vocabulary") | |||
added_tok_encoder = dict((tok, len(self) + i) for i, tok in enumerate(tokens_to_add)) | |||
added_tok_decoder = {v: k for k, v in added_tok_encoder.items()} | |||
self.added_tokens_encoder.update(added_tok_encoder) | |||
self.added_tokens_decoder.update(added_tok_decoder) | |||
# Make sure we don't split on any special tokens (even they were already in the vocab before e.g. for Albert) | |||
if special_tokens: | |||
if len(new_tokens) == 1: | |||
_insert_one_token_to_ordered_list(self.unique_no_split_tokens, new_tokens[0]) | |||
else: | |||
self.unique_no_split_tokens = sorted(set(self.unique_no_split_tokens).union(set(new_tokens))) | |||
else: | |||
# Or on the newly added tokens | |||
if len(tokens_to_add) == 1: | |||
_insert_one_token_to_ordered_list(self.unique_no_split_tokens, tokens_to_add[0]) | |||
else: | |||
self.unique_no_split_tokens = sorted(set(self.unique_no_split_tokens).union(set(tokens_to_add))) | |||
self._create_trie(self.unique_no_split_tokens) | |||
return len(tokens_to_add) | |||
def _create_trie(self, unique_no_split_tokens): | |||
trie = Trie() | |||
for token in unique_no_split_tokens: | |||
if hasattr(self, "do_lower_case") and self.do_lower_case and token not in self.all_special_tokens: | |||
trie.add(token.lower()) | |||
else: | |||
trie.add(token) | |||
self.tokens_trie = trie | |||
def num_special_tokens_to_add(self, pair: bool = False) -> int: | |||
""" | |||
Returns the number of added tokens when encoding a sequence with special tokens. | |||
.. note:: | |||
This encodes a dummy input and checks the number of added tokens, and is therefore not efficient. Do not | |||
put this inside your training loop. | |||
Args: | |||
pair (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether the number of added tokens should be computed in the case of a sequence pair or a single | |||
sequence. | |||
Returns: | |||
:obj:`int`: Number of special tokens added to sequences. | |||
""" | |||
token_ids_0 = [] | |||
token_ids_1 = [] | |||
return len(self.build_inputs_with_special_tokens(token_ids_0, token_ids_1 if pair else None)) | |||
def tokenize(self, text: TextInput, **kwargs) -> List[str]: | |||
""" | |||
Converts a string in a sequence of tokens, using the tokenizer. | |||
Split in words for word-based vocabulary or sub-words for sub-word-based vocabularies | |||
(BPE/SentencePieces/WordPieces). Takes care of added tokens. | |||
Args: | |||
text (:obj:`str`): | |||
The sequence to be encoded. | |||
**kwargs (additional keyword arguments): | |||
Passed along to the model-specific ``prepare_for_tokenization`` preprocessing method. | |||
Returns: | |||
:obj:`List[str]`: The list of tokens. | |||
""" | |||
# Simple mapping string => AddedToken for special tokens with specific tokenization behaviors | |||
all_special_tokens_extended = dict( | |||
(str(t), t) for t in self.all_special_tokens_extended if isinstance(t, AddedToken) | |||
) | |||
text, kwargs = self.prepare_for_tokenization(text, **kwargs) | |||
if kwargs: | |||
logger.warning(f"Keyword arguments {kwargs} not recognized.") | |||
# TODO: should this be in the base class? | |||
if hasattr(self, "do_lower_case") and self.do_lower_case: | |||
# convert non-special tokens to lowercase | |||
escaped_special_toks = [ | |||
re.escape(s_tok) for s_tok in (self.unique_no_split_tokens + self.all_special_tokens) | |||
] | |||
pattern = r"(" + r"|".join(escaped_special_toks) + r")|" + r"(.+?)" | |||
text = re.sub(pattern, lambda m: m.groups()[0] or m.groups()[1].lower(), text) | |||
no_split_token = set(self.unique_no_split_tokens) | |||
tokens = self.tokens_trie.split(text) | |||
# ["This is something", "<special_token_1>", " else"] | |||
for i, token in enumerate(tokens): | |||
if token in no_split_token: | |||
tok_extended = all_special_tokens_extended.get(token, None) | |||
left = tokens[i - 1] if i > 0 else None | |||
right = tokens[i + 1] if i < len(tokens) - 1 else None | |||
if isinstance(tok_extended, AddedToken): | |||
if tok_extended.rstrip and right: | |||
# A bit counter-intuitive but we strip the left of the string | |||
# since tok_extended.rstrip means the special token is eating all white spaces on its right | |||
tokens[i + 1] = right.lstrip() | |||
# Strip white spaces on the left | |||
if tok_extended.lstrip and left: | |||
tokens[i - 1] = left.rstrip() # Opposite here | |||
else: | |||
# We strip left and right by default | |||
if right: | |||
tokens[i + 1] = right.lstrip() | |||
if left: | |||
tokens[i - 1] = left.rstrip() | |||
# ["This is something", "<special_token_1>", "else"] | |||
tokenized_text = [] | |||
for token in tokens: | |||
# Need to skip eventual empty (fully stripped) tokens | |||
if not token: | |||
continue | |||
if token in no_split_token: | |||
tokenized_text.append(token) | |||
else: | |||
tokenized_text.extend(self._tokenize(token)) | |||
# ["This", " is", " something", "<special_token_1>", "else"] | |||
return tokenized_text | |||
def _tokenize(self, text, **kwargs): | |||
""" | |||
Converts a string in a sequence of tokens (string), using the tokenizer. Split in words for word-based | |||
vocabulary or sub-words for sub-word-based vocabularies (BPE/SentencePieces/WordPieces). | |||
Do NOT take care of added tokens. | |||
""" | |||
raise NotImplementedError | |||
def convert_tokens_to_ids(self, tokens: Union[str, List[str]]) -> Union[int, List[int]]: | |||
""" | |||
Converts a token string (or a sequence of tokens) in a single integer id (or a sequence of ids), using the | |||
vocabulary. | |||
Args: | |||
tokens (:obj:`str` or :obj:`List[str]`): One or several token(s) to convert to token id(s). | |||
Returns: | |||
:obj:`int` or :obj:`List[int]`: The token id or list of token ids. | |||
""" | |||
if tokens is None: | |||
return None | |||
if isinstance(tokens, str): | |||
return self._convert_token_to_id_with_added_voc(tokens) | |||
ids = [] | |||
for token in tokens: | |||
ids.append(self._convert_token_to_id_with_added_voc(token)) | |||
return ids | |||
def _convert_token_to_id_with_added_voc(self, token): | |||
if token is None: | |||
return None | |||
if token in self.added_tokens_encoder: | |||
return self.added_tokens_encoder[token] | |||
return self._convert_token_to_id(token) | |||
def _convert_token_to_id(self, token): | |||
raise NotImplementedError | |||
def _encode_plus( | |||
self, | |||
text: Union[TextInput, PreTokenizedInput, EncodedInput], | |||
text_pair: Optional[Union[TextInput, PreTokenizedInput, EncodedInput]] = 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, | |||
is_split_into_words: bool = False, | |||
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, | |||
**kwargs | |||
) -> BatchEncoding: | |||
def get_input_ids(text): | |||
if isinstance(text, str): | |||
tokens = self.tokenize(text, **kwargs) | |||
return self.convert_tokens_to_ids(tokens) | |||
elif isinstance(text, (list, tuple)) and len(text) > 0 and isinstance(text[0], str): | |||
if is_split_into_words: | |||
tokens = list( | |||
itertools.chain(*(self.tokenize(t, is_split_into_words=True, **kwargs) for t in text)) | |||
) | |||
return self.convert_tokens_to_ids(tokens) | |||
else: | |||
return self.convert_tokens_to_ids(text) | |||
elif isinstance(text, (list, tuple)) and len(text) > 0 and isinstance(text[0], int): | |||
return text | |||
else: | |||
if is_split_into_words: | |||
raise ValueError( | |||
f"Input {text} is not valid. Should be a string or a list/tuple of strings when `is_split_into_words=True`." | |||
) | |||
else: | |||
raise ValueError( | |||
f"Input {text} is not valid. Should be a string, a list/tuple of strings or a list/tuple of integers." | |||
) | |||
if return_offsets_mapping: | |||
raise NotImplementedError( | |||
"return_offset_mapping is not available when using Python tokenizers." | |||
"To use this feature, change your tokenizer to one deriving from " | |||
"transformers.PreTrainedTokenizerFast." | |||
"More information on available tokenizers at " | |||
"https://github.com/huggingface/transformers/pull/2674" | |||
) | |||
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( | |||
first_ids, | |||
pair_ids=second_ids, | |||
add_special_tokens=add_special_tokens, | |||
padding=padding_strategy.value, | |||
truncation=truncation_strategy.value, | |||
max_length=max_length, | |||
stride=stride, | |||
pad_to_multiple_of=pad_to_multiple_of, | |||
return_tensors=return_tensors, | |||
prepend_batch_axis=True, | |||
return_attention_mask=return_attention_mask, | |||
return_token_type_ids=return_token_type_ids, | |||
return_overflowing_tokens=return_overflowing_tokens, | |||
return_special_tokens_mask=return_special_tokens_mask, | |||
return_length=return_length, | |||
verbose=verbose, | |||
) | |||
def _batch_encode_plus( | |||
self, | |||
batch_text_or_text_pairs: Union[ | |||
List[TextInput], | |||
List[TextInputPair], | |||
List[PreTokenizedInput], | |||
List[PreTokenizedInputPair], | |||
List[EncodedInput], | |||
List[EncodedInputPair], | |||
], | |||
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, | |||
is_split_into_words: bool = False, | |||
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, | |||
**kwargs | |||
) -> BatchEncoding: | |||
def get_input_ids(text): | |||
if isinstance(text, str): | |||
tokens = self.tokenize(text, **kwargs) | |||
return self.convert_tokens_to_ids(tokens) | |||
elif isinstance(text, (list, tuple)) and len(text) > 0 and isinstance(text[0], str): | |||
if is_split_into_words: | |||
tokens = list( | |||
itertools.chain(*(self.tokenize(t, is_split_into_words=True, **kwargs) for t in text)) | |||
) | |||
return self.convert_tokens_to_ids(tokens) | |||
else: | |||
return self.convert_tokens_to_ids(text) | |||
elif isinstance(text, (list, tuple)) and len(text) > 0 and isinstance(text[0], int): | |||
return text | |||
else: | |||
raise ValueError( | |||
"Input is not valid. Should be a string, a list/tuple of strings or a list/tuple of integers." | |||
) | |||
if return_offsets_mapping: | |||
raise NotImplementedError( | |||
"return_offset_mapping is not available when using Python tokenizers." | |||
"To use this feature, change your tokenizer to one deriving from " | |||
"transformers.PreTrainedTokenizerFast." | |||
) | |||
input_ids = [] | |||
for ids_or_pair_ids in batch_text_or_text_pairs: | |||
if not isinstance(ids_or_pair_ids, (list, tuple)): | |||
ids, pair_ids = ids_or_pair_ids, None | |||
elif is_split_into_words and not isinstance(ids_or_pair_ids[0], (list, tuple)): | |||
ids, pair_ids = ids_or_pair_ids, None | |||
else: | |||
ids, pair_ids = ids_or_pair_ids | |||
first_ids = get_input_ids(ids) | |||
second_ids = get_input_ids(pair_ids) if pair_ids is not None else None | |||
input_ids.append((first_ids, second_ids)) | |||
batch_outputs = self._batch_prepare_for_model( | |||
input_ids, | |||
add_special_tokens=add_special_tokens, | |||
padding_strategy=padding_strategy, | |||
truncation_strategy=truncation_strategy, | |||
max_length=max_length, | |||
stride=stride, | |||
pad_to_multiple_of=pad_to_multiple_of, | |||
return_attention_mask=return_attention_mask, | |||
return_token_type_ids=return_token_type_ids, | |||
return_overflowing_tokens=return_overflowing_tokens, | |||
return_special_tokens_mask=return_special_tokens_mask, | |||
return_length=return_length, | |||
return_tensors=return_tensors, | |||
verbose=verbose, | |||
) | |||
return BatchEncoding(batch_outputs) | |||
@add_end_docstrings(ENCODE_KWARGS_DOCSTRING, ENCODE_PLUS_ADDITIONAL_KWARGS_DOCSTRING) | |||
def _batch_prepare_for_model( | |||
self, | |||
batch_ids_pairs: List[Union[PreTokenizedInputPair, Tuple[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, | |||
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: | |||
batch_ids_pairs: list of tokenized input ids or input ids pairs | |||
""" | |||
batch_outputs = {} | |||
for first_ids, second_ids in batch_ids_pairs: | |||
outputs = self.prepare_for_model( | |||
first_ids, | |||
second_ids, | |||
add_special_tokens=add_special_tokens, | |||
padding=PaddingStrategy.DO_NOT_PAD.value, # we pad in batch afterward | |||
truncation=truncation_strategy.value, | |||
max_length=max_length, | |||
stride=stride, | |||
pad_to_multiple_of=None, # we pad in batch afterward | |||
return_attention_mask=False, # we pad in batch afterward | |||
return_token_type_ids=return_token_type_ids, | |||
return_overflowing_tokens=return_overflowing_tokens, | |||
return_special_tokens_mask=return_special_tokens_mask, | |||
return_length=return_length, | |||
return_tensors=None, # We convert the whole batch to tensors at the end | |||
prepend_batch_axis=False, | |||
verbose=verbose, | |||
) | |||
for key, value in outputs.items(): | |||
if key not in batch_outputs: | |||
batch_outputs[key] = [] | |||
batch_outputs[key].append(value) | |||
batch_outputs = self.pad( | |||
batch_outputs, | |||
padding=padding_strategy.value, | |||
max_length=max_length, | |||
pad_to_multiple_of=pad_to_multiple_of, | |||
return_attention_mask=return_attention_mask, | |||
) | |||
batch_outputs = BatchEncoding(batch_outputs, tensor_type=return_tensors) | |||
return batch_outputs | |||
def prepare_for_tokenization( | |||
self, text: str, is_split_into_words: bool = False, **kwargs | |||
) -> Tuple[str, Dict[str, Any]]: | |||
""" | |||
Performs any necessary transformations before tokenization. | |||
This method should pop the arguments from kwargs and return the remaining :obj:`kwargs` as well. We test the | |||
:obj:`kwargs` at the end of the encoding process to be sure all the arguments have been used. | |||
Args: | |||
text (:obj:`str`): | |||
The text to prepare. | |||
is_split_into_words (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not the input is already pre-tokenized (e.g., split into words). If set to :obj:`True`, the | |||
tokenizer assumes the input is already split into words (for instance, by splitting it on whitespace) | |||
which it will tokenize. This is useful for NER or token classification. | |||
kwargs: | |||
Keyword arguments to use for the tokenization. | |||
Returns: | |||
:obj:`Tuple[str, Dict[str, Any]]`: The prepared text and the unused kwargs. | |||
""" | |||
return (text, kwargs) | |||
def get_special_tokens_mask( | |||
self, token_ids_0: List, token_ids_1: Optional[List] = None, already_has_special_tokens: bool = False | |||
) -> List[int]: | |||
""" | |||
Retrieves sequence ids from a token list that has no special tokens added. This method is called when adding | |||
special tokens using the tokenizer ``prepare_for_model`` or ``encode_plus`` methods. | |||
Args: | |||
token_ids_0 (:obj:`List[int]`): | |||
List of ids of the first sequence. | |||
token_ids_1 (:obj:`List[int]`, `optional`): | |||
List of ids of the second sequence. | |||
already_has_special_tokens (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not the token list is already formatted with special tokens for the model. | |||
Returns: | |||
A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token. | |||
""" | |||
if already_has_special_tokens: | |||
if token_ids_1 is not None: | |||
raise ValueError( | |||
"You should not supply a second sequence if the provided sequence of " | |||
"ids is already formatted with special tokens for the model." | |||
) | |||
return super().get_special_tokens_mask( | |||
token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True | |||
) | |||
return [0] * ((len(token_ids_1) if token_ids_1 else 0) + len(token_ids_0)) | |||
@overload | |||
def convert_ids_to_tokens(self, ids: int, skip_special_tokens: bool = False) -> str: | |||
... | |||
@overload | |||
def convert_ids_to_tokens(self, ids: List[int], skip_special_tokens: bool = False) -> List[str]: | |||
... | |||
def convert_ids_to_tokens( | |||
self, ids: Union[int, List[int]], skip_special_tokens: bool = False | |||
) -> Union[str, List[str]]: | |||
""" | |||
Converts a single index or a sequence of indices in a token or a sequence of tokens, using the vocabulary and | |||
added tokens. | |||
Args: | |||
ids (:obj:`int` or :obj:`List[int]`): | |||
The token id (or token ids) to convert to tokens. | |||
skip_special_tokens (:obj:`bool`, `optional`, defaults to :obj:`False`): | |||
Whether or not to remove special tokens in the decoding. | |||
Returns: | |||
:obj:`str` or :obj:`List[str]`: The decoded token(s). | |||
""" | |||
if isinstance(ids, int): | |||
if ids in self.added_tokens_decoder: | |||
return self.added_tokens_decoder[ids] | |||
else: | |||
return self._convert_id_to_token(ids) | |||
tokens = [] | |||
for index in ids: | |||
index = int(index) | |||
if skip_special_tokens and index in self.all_special_ids: | |||
continue | |||
if index in self.added_tokens_decoder: | |||
tokens.append(self.added_tokens_decoder[index]) | |||
else: | |||
tokens.append(self._convert_id_to_token(index)) | |||
return tokens | |||
def _convert_id_to_token(self, index: int) -> str: | |||
raise NotImplementedError | |||
def convert_tokens_to_string(self, tokens: List[str]) -> str: | |||
return " ".join(tokens) | |||
def _decode( | |||
self, | |||
token_ids: List[int], | |||
skip_special_tokens: bool = False, | |||
clean_up_tokenization_spaces: bool = True, | |||
spaces_between_special_tokens: bool = True, | |||
**kwargs | |||
) -> str: | |||
self._decode_use_source_tokenizer = kwargs.pop("use_source_tokenizer", False) | |||
filtered_tokens = self.convert_ids_to_tokens(token_ids, skip_special_tokens=skip_special_tokens) | |||
# To avoid mixing byte-level and unicode for byte-level BPT | |||
# we need to build string separately for added tokens and byte-level tokens | |||
# cf. https://github.com/huggingface/transformers/issues/1133 | |||
sub_texts = [] | |||
current_sub_text = [] | |||
for token in filtered_tokens: | |||
if skip_special_tokens and token in self.all_special_ids: | |||
continue | |||
if token in self.added_tokens_encoder: | |||
if current_sub_text: | |||
sub_texts.append(self.convert_tokens_to_string(current_sub_text)) | |||
current_sub_text = [] | |||
sub_texts.append(token) | |||
else: | |||
current_sub_text.append(token) | |||
if current_sub_text: | |||
sub_texts.append(self.convert_tokens_to_string(current_sub_text)) | |||
if spaces_between_special_tokens: | |||
text = " ".join(sub_texts) | |||
else: | |||
text = "".join(sub_texts) | |||
if clean_up_tokenization_spaces: | |||
clean_text = self.clean_up_tokenization(text) | |||
return clean_text | |||
else: | |||
return text |
@@ -0,0 +1,54 @@ | |||
# coding=utf-8 | |||
# Copyright 2020 The HuggingFace Team. 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. | |||
from math import ceil | |||
def assert_device_map(device_map, num_blocks): | |||
blocks = list(range(0, num_blocks)) | |||
device_map_blocks = [item for sublist in list(device_map.values()) for item in sublist] | |||
# Duplicate check | |||
duplicate_blocks = [] | |||
for i in device_map_blocks: | |||
if device_map_blocks.count(i) > 1 and i not in duplicate_blocks: | |||
duplicate_blocks.append(i) | |||
# Missing blocks | |||
missing_blocks = [i for i in blocks if i not in device_map_blocks] | |||
extra_blocks = [i for i in device_map_blocks if i not in blocks] | |||
assert len(duplicate_blocks) == 0, ( | |||
"Duplicate attention blocks specified in device_map. Attention blocks must be specified to one device. These " | |||
"attention blocks were specified more than once: " + str(duplicate_blocks) | |||
) | |||
assert len(missing_blocks) == 0, ( | |||
"There are attention blocks for this model that are not specified in the device_map. Add these attention " | |||
"blocks to a device on the device_map: " + str(missing_blocks) | |||
) | |||
assert ( | |||
len(extra_blocks) == 0 | |||
), "The device_map contains more attention blocks than this model has. Remove these from the device_map:" + str( | |||
extra_blocks | |||
) | |||
def get_device_map(n_layers, devices): | |||
"""Returns a dictionary of layers distributed evenly across all devices.""" | |||
layers = list(range(n_layers)) | |||
n_blocks = int(ceil(n_layers / len(devices))) | |||
layers_list = list(layers[i : i + n_blocks] for i in range(0, n_layers, n_blocks)) | |||
return dict(zip(devices, layers_list)) |
@@ -0,0 +1,120 @@ | |||
# Copyright 2020 The HuggingFace Team. 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. | |||
""" | |||
Utilities for working with package versions | |||
""" | |||
import operator | |||
import re | |||
import sys | |||
from typing import Optional | |||
from packaging import version | |||
# The package importlib_metadata is in a different place, depending on the python version. | |||
if sys.version_info < (3, 8): | |||
import importlib_metadata | |||
else: | |||
import importlib.metadata as importlib_metadata | |||
ops = { | |||
"<": operator.lt, | |||
"<=": operator.le, | |||
"==": operator.eq, | |||
"!=": operator.ne, | |||
">=": operator.ge, | |||
">": operator.gt, | |||
} | |||
def _compare_versions(op, got_ver, want_ver, requirement, pkg, hint): | |||
if got_ver is None: | |||
raise ValueError("got_ver is None") | |||
if want_ver is None: | |||
raise ValueError("want_ver is None") | |||
if not ops[op](version.parse(got_ver), version.parse(want_ver)): | |||
raise ImportError( | |||
f"{requirement} is required for a normal functioning of this module, but found {pkg}=={got_ver}.{hint}" | |||
) | |||
def require_version(requirement: str, hint: Optional[str] = None) -> None: | |||
""" | |||
Perform a runtime check of the dependency versions, using the exact same syntax used by pip. | |||
The installed module version comes from the `site-packages` dir via `importlib_metadata`. | |||
Args: | |||
requirement (:obj:`str`): pip style definition, e.g., "tokenizers==0.9.4", "tqdm>=4.27", "numpy" | |||
hint (:obj:`str`, `optional`): what suggestion to print in case of requirements not being met | |||
Example:: | |||
require_version("pandas>1.1.2") | |||
require_version("numpy>1.18.5", "this is important to have for whatever reason") | |||
""" | |||
hint = f"\n{hint}" if hint is not None else "" | |||
# non-versioned check | |||
if re.match(r"^[\w_\-\d]+$", requirement): | |||
pkg, op, want_ver = requirement, None, None | |||
else: | |||
match = re.findall(r"^([^!=<>\s]+)([\s!=<>]{1,2}.+)", requirement) | |||
if not match: | |||
raise ValueError( | |||
f"requirement needs to be in the pip package format, .e.g., package_a==1.23, or package_b>=1.23, but got {requirement}" | |||
) | |||
pkg, want_full = match[0] | |||
want_range = want_full.split(",") # there could be multiple requirements | |||
wanted = {} | |||
for w in want_range: | |||
match = re.findall(r"^([\s!=<>]{1,2})(.+)", w) | |||
if not match: | |||
raise ValueError( | |||
f"requirement needs to be in the pip package format, .e.g., package_a==1.23, or package_b>=1.23, but got {requirement}" | |||
) | |||
op, want_ver = match[0] | |||
wanted[op] = want_ver | |||
if op not in ops: | |||
raise ValueError(f"{requirement}: need one of {list(ops.keys())}, but got {op}") | |||
# special case | |||
if pkg == "python": | |||
got_ver = ".".join([str(x) for x in sys.version_info[:3]]) | |||
for op, want_ver in wanted.items(): | |||
_compare_versions(op, got_ver, want_ver, requirement, pkg, hint) | |||
return | |||
# check if any version is installed | |||
try: | |||
got_ver = importlib_metadata.version(pkg) | |||
except importlib_metadata.PackageNotFoundError: | |||
raise importlib_metadata.PackageNotFoundError( | |||
f"The '{requirement}' distribution was not found and is required by this application. {hint}" | |||
) | |||
# check that the right version is installed if version number or a range was provided | |||
if want_ver is not None: | |||
for op, want_ver in wanted.items(): | |||
_compare_versions(op, got_ver, want_ver, requirement, pkg, hint) | |||
def require_version_core(requirement): | |||
"""require_version wrapper which emits a core-specific hint on failure""" | |||
hint = "Try: pip install transformers -U or pip install -e '.[dev]' if you're working with git master" | |||
return require_version(requirement, hint) |