|
from typing import Dict, List, Optional, Tuple, Union |
|
|
|
from tokenizers import AddedToken, EncodeInput, Encoding, InputSequence, Tokenizer |
|
from tokenizers.decoders import Decoder |
|
from tokenizers.models import Model |
|
from tokenizers.normalizers import Normalizer |
|
from tokenizers.pre_tokenizers import PreTokenizer |
|
from tokenizers.processors import PostProcessor |
|
|
|
|
|
Offsets = Tuple[int, int] |
|
|
|
|
|
class BaseTokenizer: |
|
def __init__(self, tokenizer: Tokenizer, parameters=None): |
|
self._tokenizer = tokenizer |
|
self._parameters = parameters if parameters is not None else {} |
|
|
|
def __repr__(self): |
|
return "Tokenizer(vocabulary_size={}, {})".format( |
|
self._tokenizer.get_vocab_size(), |
|
", ".join(k + "=" + str(v) for k, v in self._parameters.items()), |
|
) |
|
|
|
def num_special_tokens_to_add(self, is_pair: bool) -> int: |
|
""" |
|
Return the number of special tokens that would be added for single/pair sentences. |
|
:param is_pair: Boolean indicating if the input would be a single sentence or a pair |
|
:return: |
|
""" |
|
return self._tokenizer.num_special_tokens_to_add(is_pair) |
|
|
|
def get_vocab(self, with_added_tokens: bool = True) -> Dict[str, int]: |
|
"""Returns the vocabulary |
|
|
|
Args: |
|
with_added_tokens: boolean: |
|
Whether to include the added tokens in the vocabulary |
|
|
|
Returns: |
|
The vocabulary |
|
""" |
|
return self._tokenizer.get_vocab(with_added_tokens=with_added_tokens) |
|
|
|
def get_added_tokens_decoder(self) -> Dict[int, AddedToken]: |
|
"""Returns the added reverse vocabulary |
|
|
|
Returns: |
|
The added vocabulary mapping ints to AddedTokens |
|
""" |
|
return self._tokenizer.get_added_tokens_decoder() |
|
|
|
def get_vocab_size(self, with_added_tokens: bool = True) -> int: |
|
"""Return the size of vocabulary, with or without added tokens. |
|
|
|
Args: |
|
with_added_tokens: (`optional`) bool: |
|
Whether to count in added special tokens or not |
|
|
|
Returns: |
|
Size of vocabulary |
|
""" |
|
return self._tokenizer.get_vocab_size(with_added_tokens=with_added_tokens) |
|
|
|
def enable_padding( |
|
self, |
|
direction: Optional[str] = "right", |
|
pad_to_multiple_of: Optional[int] = None, |
|
pad_id: Optional[int] = 0, |
|
pad_type_id: Optional[int] = 0, |
|
pad_token: Optional[str] = "[PAD]", |
|
length: Optional[int] = None, |
|
): |
|
"""Change the padding strategy |
|
|
|
Args: |
|
direction: (`optional`) str: |
|
Can be one of: `right` or `left` |
|
|
|
pad_to_multiple_of: (`optional`) unsigned int: |
|
If specified, the padding length should always snap to the next multiple of |
|
the given value. For example if we were going to pad with a length of 250 but |
|
`pad_to_multiple_of=8` then we will pad to 256. |
|
|
|
pad_id: (`optional`) unsigned int: |
|
The indice to be used when padding |
|
|
|
pad_type_id: (`optional`) unsigned int: |
|
The type indice to be used when padding |
|
|
|
pad_token: (`optional`) str: |
|
The pad token to be used when padding |
|
|
|
length: (`optional`) unsigned int: |
|
If specified, the length at which to pad. If not specified |
|
we pad using the size of the longest sequence in a batch |
|
""" |
|
return self._tokenizer.enable_padding( |
|
direction=direction, |
|
pad_to_multiple_of=pad_to_multiple_of, |
|
pad_id=pad_id, |
|
pad_type_id=pad_type_id, |
|
pad_token=pad_token, |
|
length=length, |
|
) |
|
|
|
def no_padding(self): |
|
"""Disable padding""" |
|
return self._tokenizer.no_padding() |
|
|
|
@property |
|
def padding(self) -> Optional[dict]: |
|
"""Get the current padding parameters |
|
|
|
Returns: |
|
None if padding is disabled, a dict with the currently set parameters |
|
if the padding is enabled. |
|
""" |
|
return self._tokenizer.padding |
|
|
|
def enable_truncation(self, max_length: int, stride: Optional[int] = 0, strategy: Optional[str] = "longest_first"): |
|
"""Change the truncation options |
|
|
|
Args: |
|
max_length: unsigned int: |
|
The maximum length at which to truncate |
|
|
|
stride: (`optional`) unsigned int: |
|
The length of the previous first sequence to be included |
|
in the overflowing sequence |
|
|
|
strategy: (`optional`) str: |
|
Can be one of `longest_first`, `only_first` or `only_second` |
|
""" |
|
return self._tokenizer.enable_truncation(max_length, stride=stride, strategy=strategy) |
|
|
|
def no_truncation(self): |
|
"""Disable truncation""" |
|
return self._tokenizer.no_truncation() |
|
|
|
@property |
|
def truncation(self) -> Optional[dict]: |
|
"""Get the current truncation parameters |
|
|
|
Returns: |
|
None if truncation is disabled, a dict with the current truncation parameters if |
|
truncation is enabled |
|
""" |
|
return self._tokenizer.truncation |
|
|
|
def add_tokens(self, tokens: List[Union[str, AddedToken]]) -> int: |
|
"""Add the given tokens to the vocabulary |
|
|
|
Args: |
|
tokens: List[Union[str, AddedToken]]: |
|
A list of tokens to add to the vocabulary. Each token can either be |
|
a string, or an instance of AddedToken |
|
|
|
Returns: |
|
The number of tokens that were added to the vocabulary |
|
""" |
|
return self._tokenizer.add_tokens(tokens) |
|
|
|
def add_special_tokens(self, special_tokens: List[Union[str, AddedToken]]) -> int: |
|
"""Add the given special tokens to the vocabulary, and treat them as special tokens. |
|
|
|
The special tokens will never be processed by the model, and will be |
|
removed while decoding. |
|
|
|
Args: |
|
tokens: List[Union[str, AddedToken]]: |
|
A list of special tokens to add to the vocabulary. Each token can either be |
|
a string, or an instance of AddedToken |
|
|
|
Returns: |
|
The number of tokens that were added to the vocabulary |
|
""" |
|
return self._tokenizer.add_special_tokens(special_tokens) |
|
|
|
def normalize(self, sequence: str) -> str: |
|
"""Normalize the given sequence |
|
|
|
Args: |
|
sequence: str: |
|
The sequence to normalize |
|
|
|
Returns: |
|
The normalized string |
|
""" |
|
return self._tokenizer.normalize(sequence) |
|
|
|
def encode( |
|
self, |
|
sequence: InputSequence, |
|
pair: Optional[InputSequence] = None, |
|
is_pretokenized: bool = False, |
|
add_special_tokens: bool = True, |
|
) -> Encoding: |
|
"""Encode the given sequence and pair. This method can process raw text sequences as well |
|
as already pre-tokenized sequences. |
|
|
|
Args: |
|
sequence: InputSequence: |
|
The sequence we want to encode. This sequence can be either raw text or |
|
pre-tokenized, according to the `is_pretokenized` argument: |
|
|
|
- If `is_pretokenized=False`: `InputSequence` is expected to be `str` |
|
- If `is_pretokenized=True`: `InputSequence` is expected to be |
|
`Union[List[str], Tuple[str]]` |
|
|
|
is_pretokenized: bool: |
|
Whether the input is already pre-tokenized. |
|
|
|
add_special_tokens: bool: |
|
Whether to add the special tokens while encoding. |
|
|
|
Returns: |
|
An Encoding |
|
""" |
|
if sequence is None: |
|
raise ValueError("encode: `sequence` can't be `None`") |
|
|
|
return self._tokenizer.encode(sequence, pair, is_pretokenized, add_special_tokens) |
|
|
|
def encode_batch( |
|
self, |
|
inputs: List[EncodeInput], |
|
is_pretokenized: bool = False, |
|
add_special_tokens: bool = True, |
|
) -> List[Encoding]: |
|
"""Encode the given inputs. This method accept both raw text sequences as well as already |
|
pre-tokenized sequences. |
|
|
|
Args: |
|
inputs: List[EncodeInput]: |
|
A list of single sequences or pair sequences to encode. Each `EncodeInput` is |
|
expected to be of the following form: |
|
`Union[InputSequence, Tuple[InputSequence, InputSequence]]` |
|
|
|
Each `InputSequence` can either be raw text or pre-tokenized, |
|
according to the `is_pretokenized` argument: |
|
|
|
- If `is_pretokenized=False`: `InputSequence` is expected to be `str` |
|
- If `is_pretokenized=True`: `InputSequence` is expected to be |
|
`Union[List[str], Tuple[str]]` |
|
|
|
is_pretokenized: bool: |
|
Whether the input is already pre-tokenized. |
|
|
|
add_special_tokens: bool: |
|
Whether to add the special tokens while encoding. |
|
|
|
Returns: |
|
A list of Encoding |
|
""" |
|
|
|
if inputs is None: |
|
raise ValueError("encode_batch: `inputs` can't be `None`") |
|
|
|
return self._tokenizer.encode_batch(inputs, is_pretokenized, add_special_tokens) |
|
|
|
def decode(self, ids: List[int], skip_special_tokens: Optional[bool] = True) -> str: |
|
"""Decode the given list of ids to a string sequence |
|
|
|
Args: |
|
ids: List[unsigned int]: |
|
A list of ids to be decoded |
|
|
|
skip_special_tokens: (`optional`) boolean: |
|
Whether to remove all the special tokens from the output string |
|
|
|
Returns: |
|
The decoded string |
|
""" |
|
if ids is None: |
|
raise ValueError("None input is not valid. Should be a list of integers.") |
|
|
|
return self._tokenizer.decode(ids, skip_special_tokens=skip_special_tokens) |
|
|
|
def decode_batch(self, sequences: List[List[int]], skip_special_tokens: Optional[bool] = True) -> str: |
|
"""Decode the list of sequences to a list of string sequences |
|
|
|
Args: |
|
sequences: List[List[unsigned int]]: |
|
A list of sequence of ids to be decoded |
|
|
|
skip_special_tokens: (`optional`) boolean: |
|
Whether to remove all the special tokens from the output strings |
|
|
|
Returns: |
|
A list of decoded strings |
|
""" |
|
if sequences is None: |
|
raise ValueError("None input is not valid. Should be list of list of integers.") |
|
|
|
return self._tokenizer.decode_batch(sequences, skip_special_tokens=skip_special_tokens) |
|
|
|
def token_to_id(self, token: str) -> Optional[int]: |
|
"""Convert the given token to its corresponding id |
|
|
|
Args: |
|
token: str: |
|
The token to convert |
|
|
|
Returns: |
|
The corresponding id if it exists, None otherwise |
|
""" |
|
return self._tokenizer.token_to_id(token) |
|
|
|
def id_to_token(self, id: int) -> Optional[str]: |
|
"""Convert the given token id to its corresponding string |
|
|
|
Args: |
|
token: id: |
|
The token id to convert |
|
|
|
Returns: |
|
The corresponding string if it exists, None otherwise |
|
""" |
|
return self._tokenizer.id_to_token(id) |
|
|
|
def save_model(self, directory: str, prefix: Optional[str] = None): |
|
"""Save the current model to the given directory |
|
|
|
Args: |
|
directory: str: |
|
A path to the destination directory |
|
|
|
prefix: (Optional) str: |
|
An optional prefix, used to prefix each file name |
|
""" |
|
return self._tokenizer.model.save(directory, prefix=prefix) |
|
|
|
def save(self, path: str, pretty: bool = True): |
|
"""Save the current Tokenizer at the given path |
|
|
|
Args: |
|
path: str: |
|
A path to the destination Tokenizer file |
|
""" |
|
return self._tokenizer.save(path, pretty) |
|
|
|
def to_str(self, pretty: bool = False): |
|
"""Get a serialized JSON version of the Tokenizer as a str |
|
|
|
Args: |
|
pretty: bool: |
|
Whether the JSON string should be prettified |
|
|
|
Returns: |
|
str |
|
""" |
|
return self._tokenizer.to_str(pretty) |
|
|
|
def post_process( |
|
self, encoding: Encoding, pair: Optional[Encoding] = None, add_special_tokens: bool = True |
|
) -> Encoding: |
|
"""Apply all the post-processing steps to the given encodings. |
|
|
|
The various steps are: |
|
1. Truncate according to global params (provided to `enable_truncation`) |
|
2. Apply the PostProcessor |
|
3. Pad according to global params. (provided to `enable_padding`) |
|
|
|
Args: |
|
encoding: Encoding: |
|
The main Encoding to post process |
|
|
|
pair: Optional[Encoding]: |
|
An optional pair Encoding |
|
|
|
add_special_tokens: bool: |
|
Whether to add special tokens |
|
|
|
Returns: |
|
The resulting Encoding |
|
""" |
|
return self._tokenizer.post_process(encoding, pair, add_special_tokens) |
|
|
|
@property |
|
def model(self) -> Model: |
|
return self._tokenizer.model |
|
|
|
@model.setter |
|
def model(self, model: Model): |
|
self._tokenizer.model = model |
|
|
|
@property |
|
def normalizer(self) -> Normalizer: |
|
return self._tokenizer.normalizer |
|
|
|
@normalizer.setter |
|
def normalizer(self, normalizer: Normalizer): |
|
self._tokenizer.normalizer = normalizer |
|
|
|
@property |
|
def pre_tokenizer(self) -> PreTokenizer: |
|
return self._tokenizer.pre_tokenizer |
|
|
|
@pre_tokenizer.setter |
|
def pre_tokenizer(self, pre_tokenizer: PreTokenizer): |
|
self._tokenizer.pre_tokenizer = pre_tokenizer |
|
|
|
@property |
|
def post_processor(self) -> PostProcessor: |
|
return self._tokenizer.post_processor |
|
|
|
@post_processor.setter |
|
def post_processor(self, post_processor: PostProcessor): |
|
self._tokenizer.post_processor = post_processor |
|
|
|
@property |
|
def decoder(self) -> Decoder: |
|
return self._tokenizer.decoder |
|
|
|
@decoder.setter |
|
def decoder(self, decoder: Decoder): |
|
self._tokenizer.decoder = decoder |
|
|