CoolFace
Apppublic

DoruC/Grounded-Segment-Anything

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
tokenization_utils.py1030 linesDownload Raw Back to transformers_4_35_0
1# coding=utf-82# Copyright 2020 The HuggingFace Inc. team.3#4# Licensed under the Apache License, Version 2.0 (the "License");5# you may not use this file except in compliance with the License.6# You may obtain a copy of the License at7#8#     http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# distributed under the License is distributed on an "AS IS" BASIS,12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# See the License for the specific language governing permissions and14# limitations under the License.15"""16 Tokenization classes for python tokenizers. For fast tokenizers (provided by HuggingFace's tokenizers library) see17 tokenization_utils_fast.py18"""19import bisect20import itertools21import re22import unicodedata23from collections import OrderedDict24from typing import Any, Dict, List, Optional, Tuple, Union, overload25 26from .tokenization_utils_base import (27    ENCODE_KWARGS_DOCSTRING,28    ENCODE_PLUS_ADDITIONAL_KWARGS_DOCSTRING,29    INIT_TOKENIZER_DOCSTRING,30    AddedToken,31    BatchEncoding,32    EncodedInput,33    EncodedInputPair,34    PreTokenizedInput,35    PreTokenizedInputPair,36    PreTrainedTokenizerBase,37    TextInput,38    TextInputPair,39    TruncationStrategy,40)41from .utils import PaddingStrategy, TensorType, add_end_docstrings, logging42 43 44logger = logging.get_logger(__name__)45 46# Slow tokenizers are saved in a vocabulary plus three separated files47SPECIAL_TOKENS_MAP_FILE = "special_tokens_map.json"48ADDED_TOKENS_FILE = "added_tokens.json"49TOKENIZER_CONFIG_FILE = "tokenizer_config.json"50 51 52class Trie:53    """54    Trie in Python. Creates a Trie out of a list of words. The trie is used to split on `added_tokens` in one pass55    Loose reference https://en.wikipedia.org/wiki/Trie56    """57 58    def __init__(self):59        self.data = {}60        self._tokens = set()61 62    def add(self, word: str):63        """64        Passes over every char (utf-8 char) on word and recursively adds it to the internal `data` trie representation.65        The special key `""` is used to represent termination.66 67        This function is idempotent, adding twice the same word will leave the trie unchanged68 69        Example:70 71        ```python72        >>> trie = Trie()73        >>> trie.add("Hello 友達")74        >>> trie.data75        {"H": {"e": {"l": {"l": {"o": {" ": {"友": {"達": {"": 1}}}}}}}}}76 77        >>> trie.add("Hello")78        >>> trie.data79        {"H": {"e": {"l": {"l": {"o": {"": 1, " ": {"友": {"達": {"": 1}}}}}}}}}80        ```81        """82        if not word:83            # Prevent empty string84            return85 86        self._tokens.add(word)87        ref = self.data88        for char in word:89            ref[char] = char in ref and ref[char] or {}90            ref = ref[char]91        ref[""] = 192 93    def split(self, text: str) -> List[str]:94        """95        Will look for the words added to the trie within `text`. Output is the original string splitted along the96        boundaries of the words found.97 98        This trie will match the longest possible word first !99 100        Example:101 102        ```python103        >>> trie = Trie()104        >>> trie.split("[CLS] This is a extra_id_100")105        ["[CLS] This is a extra_id_100"]106 107        >>> trie.add("[CLS]")108        >>> trie.add("extra_id_1")109        >>> trie.add("extra_id_100")110        >>> trie.split("[CLS] This is a extra_id_100")111        ["[CLS]", " This is a ", "extra_id_100"]112        ```113        """114        # indexes are counted left of the chars index.115        # "hello", index 0, is left of h, index 1 is between h and e.116        # index 5 is right of the "o".117 118        # States are going to capture every possible start (indexes as above)119        # as keys, and have as values, a pointer to the position in the trie120        # where we're at. This is a partial match for now.121        # This enables to keep track of multiple matches while we're iterating122        # the string123        # If the trie contains, "blowing", and "lower" and we encounter the124        # string "blower", we need to split into ["b", "lower"].125        # This is where we need to keep track of multiple possible starts.126        states = OrderedDict()127 128        # This will contain every indices where we need129        # to cut.130        # We force to cut at offset 0 and len(text) (added later)131        offsets = [0]132 133        # This is used by the lookahead which needs to skip over134        # some text where the full match exceeded the place in the initial135        # for loop136        skip = 0137        # Main loop, Giving this algorithm O(n) complexity138        for current, current_char in enumerate(text):139            if skip and current < skip:140                # Prevents the lookahead for matching twice141                # like extra_id_100 and id_100142                continue143 144            # This will track every state145            # that stop matching, we need to stop tracking them.146            # If we look at "lowball", we're going to match "l" (add it to states), "o", "w", then147            # fail on "b", we need to remove 0 from the valid states.148            to_remove = set()149            # Whenever we found a match, we need to drop everything150            # this is a greedy algorithm, it will match on the first found token151            reset = False152 153            # In this case, we already have partial matches (But unfinished)154            for start, trie_pointer in states.items():155                if "" in trie_pointer:156                    # This is a final match, we need to reset and157                    # store the results in `offsets`.158 159                    # Lookahead to match longest first160                    # Important in case of extra_id_1 vs extra_id_100161                    # Here we are also actively looking for other earlier partial162                    # matches163                    # "[CLS]", "L", we need to match CLS even if L is special164                    for lookstart, looktrie_pointer in states.items():165                        if lookstart > start:166                            # This partial match is later, we can stop looking167                            break168                        elif lookstart < start:169                            # This partial match is earlier, the trie pointer170                            # was already updated, so index is + 1171                            lookahead_index = current + 1172                            end = current + 1173                        else:174                            # Here lookstart == start and175                            #      looktrie_pointer == trie_pointer176                            # It wasn't updated yet so indices are current ones177                            lookahead_index = current178                            end = current179                        next_char = text[lookahead_index] if lookahead_index < len(text) else None180                        if "" in looktrie_pointer:181                            start = lookstart182                            end = lookahead_index183                            skip = lookahead_index184 185                        while next_char in looktrie_pointer:186                            looktrie_pointer = looktrie_pointer[next_char]187                            lookahead_index += 1188                            if "" in looktrie_pointer:189                                start = lookstart190                                end = lookahead_index191                                skip = lookahead_index192 193                            if lookahead_index == len(text):194                                # End of string195                                break196                            next_char = text[lookahead_index]197                        # End lookahead198 199                    # Storing and resetting200                    offsets.append(start)201                    offsets.append(end)202                    reset = True203                    break204                elif current_char in trie_pointer:205                    # The current character being looked at has a match within the trie206                    # update the pointer (it will be stored back into states later).207                    trie_pointer = trie_pointer[current_char]208 209                    # Storing back the new pointer into the states.210                    # Partial matches got longer by one.211                    states[start] = trie_pointer212                else:213                    # The new character has not match in the trie, we need214                    # to stop keeping track of this partial match.215                    # We can't do it directly within the loop because of how216                    # python iteration works217                    to_remove.add(start)218 219            # Either clearing the full start (we found a real match)220            # Or clearing only the partial matches that didn't work.221            if reset:222                states = {}223            else:224                for start in to_remove:225                    del states[start]226 227            # If this character is a starting character within the trie228            # start keeping track of this partial match.229            if current >= skip and current_char in self.data:230                states[current] = self.data[current_char]231 232        # We have a cut at the end with states.233        for start, trie_pointer in states.items():234            if "" in trie_pointer:235                # This is a final match, we need to reset and236                # store the results in `offsets`.237                end = len(text)238                offsets.append(start)239                offsets.append(end)240                # Longest cut is always the one with lower start so the first241                # item so we need to break.242                break243 244        return self.cut_text(text, offsets)245 246    def cut_text(self, text, offsets):247        # We have all the offsets now, we just need to do the actual splitting.248        # We need to eventually add the first part of the string and the eventual249        # last part.250        offsets.append(len(text))251        tokens = []252        start = 0253        for end in offsets:254            if start > end:255                logger.error(256                    "There was a bug in Trie algorithm in tokenization. Attempting to recover. Please report it"257                    " anyway."258                )259                continue260            elif start == end:261                # This might happen if there's a match at index 0262                # we're also preventing zero-width cuts in case of two263                # consecutive matches264                continue265            tokens.append(text[start:end])266            start = end267 268        return tokens269 270 271def _is_whitespace(char):272    """Checks whether `char` is a whitespace character."""273    # \t, \n, and \r are technically control characters but we treat them274    # as whitespace since they are generally considered as such.275    if char == " " or char == "\t" or char == "\n" or char == "\r":276        return True277    cat = unicodedata.category(char)278    if cat == "Zs":279        return True280    return False281 282 283def _is_control(char):284    """Checks whether `char` is a control character."""285    # These are technically control characters but we count them as whitespace286    # characters.287    if char == "\t" or char == "\n" or char == "\r":288        return False289    cat = unicodedata.category(char)290    if cat.startswith("C"):291        return True292    return False293 294 295def _is_punctuation(char):296    """Checks whether `char` is a punctuation character."""297    cp = ord(char)298    # We treat all non-letter/number ASCII as punctuation.299    # Characters such as "^", "$", and "`" are not in the Unicode300    # Punctuation class but we treat them as punctuation anyways, for301    # consistency.302    if (cp >= 33 and cp <= 47) or (cp >= 58 and cp <= 64) or (cp >= 91 and cp <= 96) or (cp >= 123 and cp <= 126):303        return True304    cat = unicodedata.category(char)305    if cat.startswith("P"):306        return True307    return False308 309 310def _is_end_of_word(text):311    """Checks whether the last character in text is one of a punctuation, control or whitespace character."""312    last_char = text[-1]313    return bool(_is_control(last_char) | _is_punctuation(last_char) | _is_whitespace(last_char))314 315 316def _is_start_of_word(text):317    """Checks whether the first character in text is one of a punctuation, control or whitespace character."""318    first_char = text[0]319    return bool(_is_control(first_char) | _is_punctuation(first_char) | _is_whitespace(first_char))320 321 322def _insert_one_token_to_ordered_list(token_list: List[str], new_token: str):323    """324    Inserts one token to an ordered list if it does not already exist. Note: token_list must be sorted.325    """326    insertion_idx = bisect.bisect_left(token_list, new_token)327    # Checks if new_token is already in the ordered token_list328    if insertion_idx < len(token_list) and token_list[insertion_idx] == new_token:329        # new_token is in token_list, don't add330        return331    else:332        token_list.insert(insertion_idx, new_token)333 334 335@add_end_docstrings(INIT_TOKENIZER_DOCSTRING)336class PreTrainedTokenizer(PreTrainedTokenizerBase):337    """338    Base class for all slow tokenizers.339 340    Inherits from [`~tokenization_utils_base.PreTrainedTokenizerBase`].341 342    Handle all the shared methods for tokenization and special tokens as well as methods downloading/caching/loading343    pretrained tokenizers as well as adding tokens to the vocabulary.344 345    This class also contain the added tokens in a unified way on top of all tokenizers so we don't have to handle the346    specific vocabulary augmentation methods of the various underlying dictionary structures (BPE, sentencepiece...).347    """348 349    def __init__(self, **kwargs):350        # 1. Init the parent class351        super().__init__(**kwargs)352        self.tokens_trie = Trie()353 354        # 2. init `_added_tokens_decoder` if child class did not355        if not hasattr(self, "_added_tokens_decoder"):356            self._added_tokens_decoder: Dict[int, AddedToken] = {}357        # 3. if a `added_tokens_decoder` is passed, we are loading from a saved tokenizer, we overwrite358        if "added_tokens_decoder" in kwargs:359            # overwriting the class's added_tokens_decoder. This is the source of truth!360            self._added_tokens_decoder.update(kwargs.get("added_tokens_decoder"))361 362        self._added_tokens_encoder: Dict[str, int] = {k.content: v for v, k in self._added_tokens_decoder.items()}363 364        # 4. If some of the special tokens are not part of the vocab, we add them, at the end.365        # the order of addition is the same as self.SPECIAL_TOKENS_ATTRIBUTES following `tokenizers`366        self._add_tokens(self.all_special_tokens_extended, special_tokens=True)367 368        self._decode_use_source_tokenizer = False369 370    @property371    def is_fast(self) -> bool:372        return False373 374    @property375    def vocab_size(self) -> int:376        """377        `int`: Size of the base vocabulary (without the added tokens).378        """379        raise NotImplementedError380 381    @property382    def added_tokens_encoder(self) -> Dict[str, int]:383        """384        Returns the sorted mapping from string to index. The added tokens encoder is cached for performance385        optimisation in `self._added_tokens_encoder` for the slow tokenizers.386        """387        return {k.content: v for v, k in sorted(self._added_tokens_decoder.items(), key=lambda item: item[0])}388 389    @property390    def added_tokens_decoder(self) -> Dict[int, AddedToken]:391        """392        Returns the added tokens in the vocabulary as a dictionary of index to AddedToken.393 394        Returns:395            `Dict[str, int]`: The added tokens.396        """397        return dict(sorted(self._added_tokens_decoder.items(), key=lambda item: item[0]))398 399    @added_tokens_decoder.setter400    def added_tokens_decoder(self, value: Dict[int, Union[AddedToken, str]]) -> Dict[int, AddedToken]:401        # Always raise an error if string because users should define the behavior402        for index, token in value.items():403            if not isinstance(token, (str, AddedToken)) or not isinstance(index, int):404                raise ValueError(405                    f"The provided `added_tokens_decoder` has an element of type {index.__class__, token.__class__}, should be a dict of {int, Union[AddedToken, str]}"406                )407 408            self._added_tokens_decoder[index] = AddedToken(token) if isinstance(token, str) else token409            self._added_tokens_encoder[str(token)] = index410 411    def get_added_vocab(self) -> Dict[str, int]:412        """413        Returns the added tokens in the vocabulary as a dictionary of token to index. Results might be different from414        the fast call because for now we always add the tokens even if they are already in the vocabulary. This is415        something we should change.416 417        Returns:418            `Dict[str, int]`: The added tokens.419        """420        return self._added_tokens_encoder421 422    def __len__(self):423        """424        Size of the full vocabulary with the added tokens. Counts the `keys` and not the `values` because otherwise if425        there is a hole in the vocab, we will add tokenizers at a wrong index.426        """427        return len(set(self.get_vocab().keys()))428 429    def _add_tokens(self, new_tokens: Union[List[str], List[AddedToken]], special_tokens: bool = False) -> int:430        """431        Add a list of new tokens to the tokenizer class. If the new tokens are not in the vocabulary, they are added to432        it with indices starting from length of the current vocabulary. Special tokens are sometimes already in the433        vocab which is why they have to be handled specifically.434 435        Args:436            new_tokens (`List[str]`or `List[tokenizers.AddedToken]`):437                Token(s) to add in vocabulary. A token is counted as added if it's not already in the vocabulary438                (tested by checking if the tokenizer assign the index of the `unk_token` to them). If a token is part439                of the vocabulary then we simply mark this token as an `AddedToken` which allows to control the440                stripping and normalization of this token. This is NOT possible in `tokenizers`.441            special_tokens (`bool`, *optional*, defaults to `False`):442                Whether or not the tokens should be added as special tokens.443 444        Returns:445            `int`: The number of tokens actually added to the vocabulary.446 447        Examples:448 449        ```python450        # Let's see how to increase the vocabulary of Bert model and tokenizer451        tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")452        model = BertModel.from_pretrained("bert-base-uncased")453 454        num_added_toks = tokenizer.add_tokens(["new_tok1", "my_new-tok2"])455        print("We have added", num_added_toks, "tokens")456        # Note: resize_token_embeddings expects to receive the full size of the new vocabulary, i.e. the length of the tokenizer.457        model.resize_token_embeddings(len(tokenizer))458        ```"""459        added_tokens = 0460        if new_tokens is None:461            return added_tokens462        current_vocab = self.get_vocab().copy()463        new_idx = len(current_vocab)  # only call this once, len gives the last index + 1464        for token in new_tokens:465            if not isinstance(token, (str, AddedToken)):466                raise TypeError(f"Token {token} is not a string but a {type(token)}.")467            if str(token) == "":468                continue469            if isinstance(token, str):470                # for legacy AddedTokens strip left and right by default471                # TODO this will be remove to have the same default behavior as rust472                token = AddedToken(token, normalized=not special_tokens, rstrip=True, lstrip=True)473            if special_tokens:474                token.special = True475            if token in self._added_tokens_decoder:476                continue477            if not token.special and token.normalized and hasattr(self, "do_lower_case") and self.do_lower_case:478                # Normalize if requested479                token.content = token.content.lower()480            if token.content not in current_vocab:481                token_index = new_idx + added_tokens482                current_vocab[token.content] = token_index483                added_tokens += 1484            else:485                token_index = current_vocab[token.content]486 487            if token.special and str(token) not in self.all_special_tokens:488                self._additional_special_tokens.append(token)489            # the setter automatically updates the reverse map490            self._added_tokens_decoder[token_index] = token491            self._added_tokens_encoder[token.content] = token_index492            if self.verbose:493                logger.info(f"Adding {token} to the vocabulary")494 495        self._update_trie()496        return added_tokens497 498    def _update_trie(self, unique_no_split_tokens: Optional[str] = []):499        for token in self._added_tokens_decoder.values():500            if token not in self.tokens_trie._tokens:501                self.tokens_trie.add(token.content)502        for token in unique_no_split_tokens:503            if token not in self.tokens_trie._tokens:504                self.tokens_trie.add(token)505 506    def num_special_tokens_to_add(self, pair: bool = False) -> int:507        """508        Returns the number of added tokens when encoding a sequence with special tokens.509 510        <Tip>511 512        This encodes a dummy input and checks the number of added tokens, and is therefore not efficient. Do not put513        this inside your training loop.514 515        </Tip>516 517        Args:518            pair (`bool`, *optional*, defaults to `False`):519                Whether the number of added tokens should be computed in the case of a sequence pair or a single520                sequence.521 522        Returns:523            `int`: Number of special tokens added to sequences.524        """525        token_ids_0 = []526        token_ids_1 = []527        return len(self.build_inputs_with_special_tokens(token_ids_0, token_ids_1 if pair else None))528 529    def tokenize(self, text: TextInput, **kwargs) -> List[str]:530        """531        Converts a string in a sequence of tokens, using the tokenizer.532 533        Split in words for word-based vocabulary or sub-words for sub-word-based vocabularies534        (BPE/SentencePieces/WordPieces). Takes care of added tokens.535 536        Args:537            text (`str`):538                The sequence to be encoded.539            **kwargs (additional keyword arguments):540                Passed along to the model-specific `prepare_for_tokenization` preprocessing method.541 542        Returns:543            `List[str]`: The list of tokens.544        """545        split_special_tokens = kwargs.pop("split_special_tokens", self.split_special_tokens)546 547        text, kwargs = self.prepare_for_tokenization(text, **kwargs)548 549        if kwargs:550            logger.warning(f"Keyword arguments {kwargs} not recognized.")551 552        if hasattr(self, "do_lower_case") and self.do_lower_case:553            # convert non-special tokens to lowercase554            escaped_special_toks = [re.escape(s_tok) for s_tok in (self.all_special_tokens)]555            escaped_special_toks += [556                re.escape(s_tok.content)557                for s_tok in (self._added_tokens_decoder.values())558                if not s_tok.special and s_tok.normalized559            ]560            pattern = r"(" + r"|".join(escaped_special_toks) + r")|" + r"(.+?)"561            text = re.sub(pattern, lambda m: m.groups()[0] or m.groups()[1].lower(), text)562 563        if split_special_tokens:564            no_split_token = []565            tokens = [text]566        else:567            no_split_token = set(self._added_tokens_encoder.keys())  # don't split on any of the added tokens568            # "This is something<special_token_1>  else"569            tokens = self.tokens_trie.split(text)570 571        # ["This is something", "<special_token_1>", "  else"]572        for i, token in enumerate(tokens):573            if token in no_split_token:574                tok_extended = self._added_tokens_decoder.get(self._added_tokens_encoder[token], None)575                left = tokens[i - 1] if i > 0 else None576                right = tokens[i + 1] if i < len(tokens) - 1 else None577                if isinstance(tok_extended, AddedToken):578                    if tok_extended.rstrip and right:579                        # A bit counter-intuitive but we strip the left of the string580                        # since tok_extended.rstrip means the special token is eating all white spaces on its right581                        tokens[i + 1] = right.lstrip()582                    # Strip white spaces on the left583                    if tok_extended.lstrip and left:584                        tokens[i - 1] = left.rstrip()  # Opposite here585                    if tok_extended.single_word and left and left[-1] != " ":586                        tokens[i - 1] += token587                        tokens[i] = ""588                    elif tok_extended.single_word and right and right[0] != " ":589                        tokens[i + 1] = token + tokens[i + 1]590                        tokens[i] = ""591 592                else:593                    raise ValueError(594                        f"{tok_extended} cannot be tokenized because it was not properly added"595                        f" to the tokenizer. This means that it is not an `AddedToken` but a {type(tok_extended)}"596                    )597        # ["This is something", "<special_token_1>", "else"]598        tokenized_text = []599        for token in tokens:600            # Need to skip eventual empty (fully stripped) tokens601            if not token:602                continue603            if token in no_split_token:604                tokenized_text.append(token)605            else:606                tokenized_text.extend(self._tokenize(token))607        # ["This", " is", " something", "<special_token_1>", "else"]608        return tokenized_text609 610    def _tokenize(self, text, **kwargs):611        """612        Converts a string in a sequence of tokens (string), using the tokenizer. Split in words for word-based613        vocabulary or sub-words for sub-word-based vocabularies (BPE/SentencePieces/WordPieces).614 615        Do NOT take care of added tokens.616        """617        raise NotImplementedError618 619    def convert_tokens_to_ids(self, tokens: Union[str, List[str]]) -> Union[int, List[int]]:620        """621        Converts a token string (or a sequence of tokens) in a single integer id (or a sequence of ids), using the622        vocabulary.623 624        Args:625            tokens (`str` or `List[str]`): One or several token(s) to convert to token id(s).626 627        Returns:628            `int` or `List[int]`: The token id or list of token ids.629        """630        if tokens is None:631            return None632 633        if isinstance(tokens, str):634            return self._convert_token_to_id_with_added_voc(tokens)635 636        ids = []637        for token in tokens:638            ids.append(self._convert_token_to_id_with_added_voc(token))639        return ids640 641    def _convert_token_to_id_with_added_voc(self, token):642        if token is None:643            return None644 645        if token in self._added_tokens_encoder:646            return self._added_tokens_encoder[token]647        return self._convert_token_to_id(token)648 649    def _convert_token_to_id(self, token):650        raise NotImplementedError651 652    def _encode_plus(653        self,654        text: Union[TextInput, PreTokenizedInput, EncodedInput],655        text_pair: Optional[Union[TextInput, PreTokenizedInput, EncodedInput]] = None,656        add_special_tokens: bool = True,657        padding_strategy: PaddingStrategy = PaddingStrategy.DO_NOT_PAD,658        truncation_strategy: TruncationStrategy = TruncationStrategy.DO_NOT_TRUNCATE,659        max_length: Optional[int] = None,660        stride: int = 0,661        is_split_into_words: bool = False,662        pad_to_multiple_of: Optional[int] = None,663        return_tensors: Optional[Union[str, TensorType]] = None,664        return_token_type_ids: Optional[bool] = None,665        return_attention_mask: Optional[bool] = None,666        return_overflowing_tokens: bool = False,667        return_special_tokens_mask: bool = False,668        return_offsets_mapping: bool = False,669        return_length: bool = False,670        verbose: bool = True,671        **kwargs,672    ) -> BatchEncoding:673        def get_input_ids(text):674            if isinstance(text, str):675                tokens = self.tokenize(text, **kwargs)676                return self.convert_tokens_to_ids(tokens)677            elif isinstance(text, (list, tuple)) and len(text) > 0 and isinstance(text[0], str):678                if is_split_into_words:679                    tokens = list(680                        itertools.chain(*(self.tokenize(t, is_split_into_words=True, **kwargs) for t in text))681                    )682                    return self.convert_tokens_to_ids(tokens)683                else:684                    return self.convert_tokens_to_ids(text)685            elif isinstance(text, (list, tuple)) and len(text) > 0 and isinstance(text[0], int):686                return text687            else:688                if is_split_into_words:689                    raise ValueError(690                        f"Input {text} is not valid. Should be a string or a list/tuple of strings when"691                        " `is_split_into_words=True`."692                    )693                else:694                    raise ValueError(695                        f"Input {text} is not valid. Should be a string, a list/tuple of strings or a list/tuple of"696                        " integers."697                    )698 699        if return_offsets_mapping:700            raise NotImplementedError(701                "return_offset_mapping is not available when using Python tokenizers. "702                "To use this feature, change your tokenizer to one deriving from "703                "transformers.PreTrainedTokenizerFast. "704                "More information on available tokenizers at "705                "https://github.com/huggingface/transformers/pull/2674"706            )707 708        first_ids = get_input_ids(text)709        second_ids = get_input_ids(text_pair) if text_pair is not None else None710 711        return self.prepare_for_model(712            first_ids,713            pair_ids=second_ids,714            add_special_tokens=add_special_tokens,715            padding=padding_strategy.value,716            truncation=truncation_strategy.value,717            max_length=max_length,718            stride=stride,719            pad_to_multiple_of=pad_to_multiple_of,720            return_tensors=return_tensors,721            prepend_batch_axis=True,722            return_attention_mask=return_attention_mask,723            return_token_type_ids=return_token_type_ids,724            return_overflowing_tokens=return_overflowing_tokens,725            return_special_tokens_mask=return_special_tokens_mask,726            return_length=return_length,727            verbose=verbose,728        )729 730    def _batch_encode_plus(731        self,732        batch_text_or_text_pairs: Union[733            List[TextInput],734            List[TextInputPair],735            List[PreTokenizedInput],736            List[PreTokenizedInputPair],737            List[EncodedInput],738            List[EncodedInputPair],739        ],740        add_special_tokens: bool = True,741        padding_strategy: PaddingStrategy = PaddingStrategy.DO_NOT_PAD,742        truncation_strategy: TruncationStrategy = TruncationStrategy.DO_NOT_TRUNCATE,743        max_length: Optional[int] = None,744        stride: int = 0,745        is_split_into_words: bool = False,746        pad_to_multiple_of: Optional[int] = None,747        return_tensors: Optional[Union[str, TensorType]] = None,748        return_token_type_ids: Optional[bool] = None,749        return_attention_mask: Optional[bool] = None,750        return_overflowing_tokens: bool = False,751        return_special_tokens_mask: bool = False,752        return_offsets_mapping: bool = False,753        return_length: bool = False,754        verbose: bool = True,755        **kwargs,756    ) -> BatchEncoding:757        def get_input_ids(text):758            if isinstance(text, str):759                tokens = self.tokenize(text, **kwargs)760                return self.convert_tokens_to_ids(tokens)761            elif isinstance(text, (list, tuple)) and len(text) > 0 and isinstance(text[0], str):762                if is_split_into_words:763                    tokens = list(764                        itertools.chain(*(self.tokenize(t, is_split_into_words=True, **kwargs) for t in text))765                    )766                    return self.convert_tokens_to_ids(tokens)767                else:768                    return self.convert_tokens_to_ids(text)769            elif isinstance(text, (list, tuple)) and len(text) > 0 and isinstance(text[0], int):770                return text771            else:772                raise ValueError(773                    "Input is not valid. Should be a string, a list/tuple of strings or a list/tuple of integers."774                )775 776        if return_offsets_mapping:777            raise NotImplementedError(778                "return_offset_mapping is not available when using Python tokenizers. "779                "To use this feature, change your tokenizer to one deriving from "780                "transformers.PreTrainedTokenizerFast."781            )782 783        input_ids = []784        for ids_or_pair_ids in batch_text_or_text_pairs:785            if not isinstance(ids_or_pair_ids, (list, tuple)):786                ids, pair_ids = ids_or_pair_ids, None787            elif is_split_into_words and not isinstance(ids_or_pair_ids[0], (list, tuple)):788                ids, pair_ids = ids_or_pair_ids, None789            else:790                ids, pair_ids = ids_or_pair_ids791 792            first_ids = get_input_ids(ids)793            second_ids = get_input_ids(pair_ids) if pair_ids is not None else None794            input_ids.append((first_ids, second_ids))795 796        batch_outputs = self._batch_prepare_for_model(797            input_ids,798            add_special_tokens=add_special_tokens,799            padding_strategy=padding_strategy,800            truncation_strategy=truncation_strategy,801            max_length=max_length,802            stride=stride,803            pad_to_multiple_of=pad_to_multiple_of,804            return_attention_mask=return_attention_mask,805            return_token_type_ids=return_token_type_ids,806            return_overflowing_tokens=return_overflowing_tokens,807            return_special_tokens_mask=return_special_tokens_mask,808            return_length=return_length,809            return_tensors=return_tensors,810            verbose=verbose,811        )812 813        return BatchEncoding(batch_outputs)814 815    @add_end_docstrings(ENCODE_KWARGS_DOCSTRING, ENCODE_PLUS_ADDITIONAL_KWARGS_DOCSTRING)816    def _batch_prepare_for_model(817        self,818        batch_ids_pairs: List[Union[PreTokenizedInputPair, Tuple[List[int], None]]],819        add_special_tokens: bool = True,820        padding_strategy: PaddingStrategy = PaddingStrategy.DO_NOT_PAD,821        truncation_strategy: TruncationStrategy = TruncationStrategy.DO_NOT_TRUNCATE,822        max_length: Optional[int] = None,823        stride: int = 0,824        pad_to_multiple_of: Optional[int] = None,825        return_tensors: Optional[str] = None,826        return_token_type_ids: Optional[bool] = None,827        return_attention_mask: Optional[bool] = None,828        return_overflowing_tokens: bool = False,829        return_special_tokens_mask: bool = False,830        return_length: bool = False,831        verbose: bool = True,832    ) -> BatchEncoding:833        """834        Prepares a sequence of input id, or a pair of sequences of inputs ids so that it can be used by the model. It835        adds special tokens, truncates sequences if overflowing while taking into account the special tokens and836        manages a moving window (with user defined stride) for overflowing tokens837 838        Args:839            batch_ids_pairs: list of tokenized input ids or input ids pairs840        """841 842        batch_outputs = {}843        for first_ids, second_ids in batch_ids_pairs:844            outputs = self.prepare_for_model(845                first_ids,846                second_ids,847                add_special_tokens=add_special_tokens,848                padding=PaddingStrategy.DO_NOT_PAD.value,  # we pad in batch afterward849                truncation=truncation_strategy.value,850                max_length=max_length,851                stride=stride,852                pad_to_multiple_of=None,  # we pad in batch afterward853                return_attention_mask=False,  # we pad in batch afterward854                return_token_type_ids=return_token_type_ids,855                return_overflowing_tokens=return_overflowing_tokens,856                return_special_tokens_mask=return_special_tokens_mask,857                return_length=return_length,858                return_tensors=None,  # We convert the whole batch to tensors at the end859                prepend_batch_axis=False,860                verbose=verbose,861            )862 863            for key, value in outputs.items():864                if key not in batch_outputs:865                    batch_outputs[key] = []866                batch_outputs[key].append(value)867 868        batch_outputs = self.pad(869            batch_outputs,870            padding=padding_strategy.value,871            max_length=max_length,872            pad_to_multiple_of=pad_to_multiple_of,873            return_attention_mask=return_attention_mask,874        )875 876        batch_outputs = BatchEncoding(batch_outputs, tensor_type=return_tensors)877 878        return batch_outputs879 880    def prepare_for_tokenization(881        self, text: str, is_split_into_words: bool = False, **kwargs882    ) -> Tuple[str, Dict[str, Any]]:883        """884        Performs any necessary transformations before tokenization.885 886        This method should pop the arguments from kwargs and return the remaining `kwargs` as well. We test the887        `kwargs` at the end of the encoding process to be sure all the arguments have been used.888 889        Args:890            text (`str`):891                The text to prepare.892            is_split_into_words (`bool`, *optional*, defaults to `False`):893                Whether or not the input is already pre-tokenized (e.g., split into words). If set to `True`, the894                tokenizer assumes the input is already split into words (for instance, by splitting it on whitespace)895                which it will tokenize. This is useful for NER or token classification.896            kwargs (`Dict[str, Any]`, *optional*):897                Keyword arguments to use for the tokenization.898 899        Returns:900            `Tuple[str, Dict[str, Any]]`: The prepared text and the unused kwargs.901        """902        return (text, kwargs)903 904    def get_special_tokens_mask(905        self, token_ids_0: List, token_ids_1: Optional[List] = None, already_has_special_tokens: bool = False906    ) -> List[int]:907        """908        Retrieves sequence ids from a token list that has no special tokens added. This method is called when adding909        special tokens using the tokenizer `prepare_for_model` or `encode_plus` methods.910 911        Args:912            token_ids_0 (`List[int]`):913                List of ids of the first sequence.914            token_ids_1 (`List[int]`, *optional*):915                List of ids of the second sequence.916            already_has_special_tokens (`bool`, *optional*, defaults to `False`):917                Whether or not the token list is already formatted with special tokens for the model.918 919        Returns:920            A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.921        """922        if already_has_special_tokens:923            if token_ids_1 is not None:924                raise ValueError(925                    "You should not supply a second sequence if the provided sequence of "926                    "ids is already formatted with special tokens for the model."927                )928 929            return super().get_special_tokens_mask(930                token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True931            )932        return [0] * ((len(token_ids_1) if token_ids_1 else 0) + len(token_ids_0))933 934    @overload935    def convert_ids_to_tokens(self, ids: int, skip_special_tokens: bool = False) -> str:936        ...937 938    @overload939    def convert_ids_to_tokens(self, ids: List[int], skip_special_tokens: bool = False) -> List[str]:940        ...941 942    def convert_ids_to_tokens(943        self, ids: Union[int, List[int]], skip_special_tokens: bool = False944    ) -> Union[str, List[str]]:945        """946        Converts a single index or a sequence of indices in a token or a sequence of tokens, using the vocabulary and947        added tokens.948 949        Args:950            ids (`int` or `List[int]`):951                The token id (or token ids) to convert to tokens.952            skip_special_tokens (`bool`, *optional*, defaults to `False`):953                Whether or not to remove special tokens in the decoding.954 955        Returns:956            `str` or `List[str]`: The decoded token(s).957        """958        if isinstance(ids, int):959            if ids in self._added_tokens_decoder:960                return self._added_tokens_decoder[ids].content961            else:962                return self._convert_id_to_token(ids)963        tokens = []964        for index in ids:965            index = int(index)966            if skip_special_tokens and index in self.all_special_ids:967                continue968            if index in self._added_tokens_decoder:969                tokens.append(self._added_tokens_decoder[index].content)970            else:971                tokens.append(self._convert_id_to_token(index))972        return tokens973 974    def _convert_id_to_token(self, index: int) -> str:975        raise NotImplementedError976 977    def convert_tokens_to_string(self, tokens: List[str]) -> str:978        return " ".join(tokens)979 980    def _decode(981        self,982        token_ids: List[int],983        skip_special_tokens: bool = False,984        clean_up_tokenization_spaces: bool = None,985        spaces_between_special_tokens: bool = True,986        **kwargs,987    ) -> str:988        self._decode_use_source_tokenizer = kwargs.pop("use_source_tokenizer", False)989 990        filtered_tokens = self.convert_ids_to_tokens(token_ids, skip_special_tokens=skip_special_tokens)991        legacy_added_tokens = set(self._added_tokens_encoder.keys()) - set(self.all_special_tokens) | {992            token for token in self.additional_special_tokens if self.convert_tokens_to_ids(token) >= self.vocab_size993        }994        # To avoid mixing byte-level and unicode for byte-level BPT995        # we need to build string separately for added tokens and byte-level tokens996        # cf. https://github.com/huggingface/transformers/issues/1133997        sub_texts = []998        current_sub_text = []999        # TODO @ArthurZ in version 5, special tokens should be handled in convert_tokens_to_string, while _convert_tokens_to_string1000        for token in filtered_tokens:1001            if skip_special_tokens and token in self.all_special_ids:1002                continue1003            if token in legacy_added_tokens:1004                if current_sub_text:1005                    string = self.convert_tokens_to_string(current_sub_text)1006                    if len(string) > 0:1007                        sub_texts.append(string)1008                    current_sub_text = []1009                sub_texts.append(token)1010            else:1011                current_sub_text.append(token)1012        if current_sub_text:1013            sub_texts.append(self.convert_tokens_to_string(current_sub_text))1014 1015        if spaces_between_special_tokens:1016            text = " ".join(sub_texts)1017        else:1018            text = "".join(sub_texts)1019 1020        clean_up_tokenization_spaces = (1021            clean_up_tokenization_spaces1022            if clean_up_tokenization_spaces is not None1023            else self.clean_up_tokenization_spaces1024        )1025        if clean_up_tokenization_spaces:1026            clean_text = self.clean_up_tokenization(text)1027            return clean_text1028        else:1029            return text1030