CoolFace
Modelpublic

CofeAI/Tele-FLM

sourceHugging Faceapache-2.0updated 9mo agoView on Hugging Face
30likes18kdownloads
tokenization_teleflm.py404 linesDownload Raw Back to root
1# coding=utf-82# Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved.3#4# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX5# and OPT implementations in this library. It has been modified from its6# original forms to accommodate minor architectural differences compared7# to GPT-NeoX and OPT used by the Meta AI team that trained the model.8#9# Licensed under the Apache License, Version 2.0 (the "License");10# you may not use this file except in compliance with the License.11# You may obtain a copy of the License at12#13#     http://www.apache.org/licenses/LICENSE-2.014#15# Unless required by applicable law or agreed to in writing, software16# distributed under the License is distributed on an "AS IS" BASIS,17# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.18# See the License for the specific language governing permissions and19# limitations under the License.20 21"""Tokenization classes for Tele-FLM."""22import os23from shutil import copyfile24from typing import Any, Dict, List, Optional, Tuple25 26import sentencepiece as spm27import re28from transformers.convert_slow_tokenizer import import_protobuf29from transformers import AddedToken, PreTrainedTokenizer30from transformers.utils import logging31from transformers.tokenization_utils_base import TextInput32 33logger = logging.get_logger(__name__)34 35VOCAB_FILES_NAMES = {"vocab_file": "tokenizer.model"}36 37PRETRAINED_VOCAB_FILES_MAP = {38    "vocab_file": {},39    "tokenizer_file": {},40}41PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES = {42    "teleflm-tokenizer": 8192,43}44SPIECE_UNDERLINE = "โ–"45 46 47class TeleFLMTokenizer(PreTrainedTokenizer):48    """49    Construct a Tele-FLM tokenizer. Based on byte-level Byte-Pair-Encoding. The default padding token is unset as there is50    no padding token in the original model.51 52    Args:53        vocab_file (`str`):54            Path to the vocabulary file.55        unk_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<unk>"`):56            The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this57            token instead.58        bos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<s>"`):59            The beginning of sequence token that was used during pretraining. Can be used a sequence classifier token.60        eos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"</s>"`):61            The end of sequence token.62        pad_token (`str` or `tokenizers.AddedToken`, *optional*):63            A special token used to make arrays of tokens the same size for batching purpose. Will then be ignored by64            attention mechanisms or loss computation.65        sp_model_kwargs (`Dict[str, Any]`, `Optional`, *optional*):66            Will be passed to the `SentencePieceProcessor.__init__()` method. The [Python wrapper for67            SentencePiece](https://github.com/google/sentencepiece/tree/master/python) can be used, among other things,68            to set:69 70            - `enable_sampling`: Enable subword regularization.71            - `nbest_size`: Sampling parameters for unigram. Invalid for BPE-Dropout.72 73              - `nbest_size = {0,1}`: No sampling is performed.74              - `nbest_size > 1`: samples from the nbest_size results.75              - `nbest_size < 0`: assuming that nbest_size is infinite and samples from the all hypothesis (lattice)76                using forward-filtering-and-backward-sampling algorithm.77 78            - `alpha`: Smoothing parameter for unigram sampling, and dropout probability of merge operations for79              BPE-dropout.80 81        add_bos_token (`bool`, *optional*, defaults to `True`):82            Whether or not to add an `bos_token` at the start of sequences.83        add_eos_token (`bool`, *optional*, defaults to `False`):84            Whether or not to add an `eos_token` at the end of sequences.85        clean_up_tokenization_spaces (`bool`, *optional*, defaults to `False`):86            Whether or not to cleanup spaces after decoding, cleanup consists in removing potential artifacts like87            extra spaces.88        spaces_between_special_tokens (`bool`, *optional*, defaults to `False`):89            Whether or not to add spaces between special tokens.90 91    """92 93    vocab_files_names = VOCAB_FILES_NAMES94    pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP95    max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES96    model_input_names = ["input_ids", "attention_mask"]97 98    def __init__(99        self,100        vocab_file,101        bos_token="<s>",102        eos_token="</s>",103        unk_token="<unk>",104        pad_token=None,105        sp_model_kwargs: Optional[Dict[str, Any]] = None,106        add_bos_token=False,107        add_eos_token=False,108        clean_up_tokenization_spaces=False,109        spaces_between_special_tokens=False,110        **kwargs,111    ):112        self.sp_model_kwargs = {} if sp_model_kwargs is None else sp_model_kwargs113        bos_token = AddedToken(bos_token, normalized=False, special=True) if isinstance(bos_token, str) else bos_token114        eos_token = AddedToken(eos_token, normalized=False, special=True) if isinstance(eos_token, str) else eos_token115        pad_token = AddedToken(pad_token, normalized=False, special=True) if isinstance(pad_token, str) else pad_token116        self.vocab_file = vocab_file117        self.add_bos_token = add_bos_token118        self.add_eos_token = add_eos_token119        self.sp_model = self.get_spm_processor(kwargs.pop("from_slow", False))120        super().__init__(121            bos_token=bos_token,122            eos_token=eos_token,123            unk_token=unk_token,124            pad_token=pad_token,125            add_bos_token=add_bos_token,126            add_eos_token=add_eos_token,127            sp_model_kwargs=self.sp_model_kwargs,128            clean_up_tokenization_spaces=clean_up_tokenization_spaces,129            spaces_between_special_tokens=spaces_between_special_tokens,130            **kwargs,131        )132 133    @property134    def unk_token_length(self):135        return len(self.sp_model.encode(str(self.unk_token)))136 137    # Copied from transformers.models.t5.tokenization_t5.T5Tokenizer.get_spm_processor138    def get_spm_processor(self, from_slow=False):139        tokenizer = spm.SentencePieceProcessor(**self.sp_model_kwargs)140        with open(self.vocab_file, "rb") as f:141            sp_model = f.read()142            model_pb2 = import_protobuf(f"The new behaviour of {self.__class__.__name__} (with `self.legacy = False`)")143            model = model_pb2.ModelProto.FromString(sp_model)144            normalizer_spec = model_pb2.NormalizerSpec()145            normalizer_spec.add_dummy_prefix = True146            model.normalizer_spec.MergeFrom(normalizer_spec)147            sp_model = model.SerializeToString()148            tokenizer.LoadFromSerializedProto(sp_model)149        return tokenizer150 151    def __getstate__(self):152        state = self.__dict__.copy()153        state["sp_model"] = None154        state["sp_model_proto"] = self.sp_model.serialized_model_proto()155        return state156 157    def __setstate__(self, d):158        self.__dict__ = d159        self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)160        self.sp_model.LoadFromSerializedProto(self.sp_model_proto)161 162    @property163    def vocab_size(self):164        """Returns vocab size"""165        return self.sp_model.get_piece_size()166 167    def get_vocab(self):168        """Returns vocab as a dict"""169        vocab = {self.convert_ids_to_tokens(i): i for i in range(self.vocab_size)}170        vocab.update(self.added_tokens_encoder)171        return vocab172 173    def tokenize(self, text: TextInput, **kwargs) -> List[str]:174        """175        Converts a string in a sequence of tokens, using the tokenizer.176 177        Split in words for word-based vocabulary or sub-words for sub-word-based vocabularies178        (BPE/SentencePieces/WordPieces). Takes care of added tokens.179 180        Args:181            text (`str`):182                The sequence to be encoded.183            **kwargs (additional keyword arguments):184                Passed along to the model-specific `prepare_for_tokenization` preprocessing method.185 186        Returns:187            `List[str]`: The list of tokens.188        """189        split_special_tokens = kwargs.pop("split_special_tokens", self.split_special_tokens)190        remove_dummy_prefix = kwargs.pop("remove_dummy_prefix", False)191 192        text, kwargs = self.prepare_for_tokenization(text, **kwargs)193 194        if kwargs:195            logger.warning(f"Keyword arguments {kwargs} not recognized.")196 197        if hasattr(self, "do_lower_case") and self.do_lower_case:198            # convert non-special tokens to lowercase. Might be super slow as well?199            escaped_special_toks = [re.escape(s_tok) for s_tok in (self.all_special_tokens)]200            escaped_special_toks += [201                re.escape(s_tok.content)202                for s_tok in (self._added_tokens_decoder.values())203                if not s_tok.special and s_tok.normalized204            ]205            pattern = r"(" + r"|".join(escaped_special_toks) + r")|" + r"(.+?)"206            text = re.sub(pattern, lambda m: m.groups()[0] or m.groups()[1].lower(), text)207 208        if split_special_tokens:209            no_split_token = []210            tokens = [text]211        else:212            no_split_token = self._added_tokens_encoder.keys()  # don't split on any of the added tokens213            # "This is something<special_token_1>  else"214            tokens = self.tokens_trie.split(text)215 216        # ["This is something", "<special_token_1>", "  else"]217        for i, token in enumerate(tokens):218            if token in no_split_token:219                tok_extended = self._added_tokens_decoder.get(self._added_tokens_encoder[token], None)220                left = tokens[i - 1] if i > 0 else None221                right = tokens[i + 1] if i < len(tokens) - 1 else None222                if isinstance(tok_extended, AddedToken):223                    if tok_extended.rstrip and right:224                        # A bit counter-intuitive but we strip the left of the string225                        # since tok_extended.rstrip means the special token is eating all white spaces on its right226                        tokens[i + 1] = right.lstrip()227                    # Strip white spaces on the left228                    if tok_extended.lstrip and left:229                        tokens[i - 1] = left.rstrip()  # Opposite here230                    if tok_extended.single_word and left and left[-1] != " ":231                        tokens[i - 1] += token232                        tokens[i] = ""233                    elif tok_extended.single_word and right and right[0] != " ":234                        tokens[i + 1] = token + tokens[i + 1]235                        tokens[i] = ""236                else:237                    raise ValueError(238                        f"{tok_extended} cannot be tokenized because it was not properly added"239                        f" to the tokenizer. This means that it is not an `AddedToken` but a {type(tok_extended)}"240                    )241        # ["This is something", "<special_token_1>", "else"]242        tokenized_text = []243        for token in tokens:244            # Need to skip eventual empty (fully stripped) tokens245            if not token:246                continue247            if token in no_split_token:248                tokenized_text.append(token)249            else:250                tokenized_text.extend(self._tokenize(token, remove_dummy_prefix=remove_dummy_prefix))251        # ["This", " is", " something", "<special_token_1>", "else"]252        return tokenized_text253 254    def _tokenize(self, text, **kwargs):255        """256        Returns a tokenized string.257 258        We add a option to remove dummpy prefix during tokenization instead of changing the default behaviour of the sentencepiece tokenizer.259        This is useful when there're two tokenized sentences to be merged into one as the last one will have an extra dummy prefix which results in a260        inconsistant pattern.261        """262        tokens = self.sp_model.encode(text, out_type=str)263        if text.startswith((SPIECE_UNDERLINE, " ")):264            return tokens265        if len(tokens) > 0 and kwargs.get("remove_dummy_prefix") is True:266            tokens[0] = tokens[0].replace(SPIECE_UNDERLINE, "", 1)267        return tokens268 269    def _convert_token_to_id(self, token):270        """Converts a token (str) in an id using the vocab."""271        return self.sp_model.piece_to_id(token)272 273    def _convert_id_to_token(self, index):274        """Converts an index (integer) in a token (str) using the vocab."""275        token = self.sp_model.IdToPiece(index)276        return token277 278    def convert_tokens_to_string(self, tokens):279        """Converts a sequence of tokens (string) in a single string."""280        current_sub_tokens = []281        out_string = ""282        # prev_is_special = False283        for i, token in enumerate(tokens):284            # make sure that special tokens are not decoded using sentencepiece model285            if token in self.all_special_tokens:286                # if not prev_is_special and i != 0 and self.legacy:287                #     out_string += " "288                out_string += self.sp_model.decode(current_sub_tokens) + token289                # prev_is_special = True290                current_sub_tokens = []291            else:292                current_sub_tokens.append(token)293                # prev_is_special = False294        out_string += self.sp_model.decode(current_sub_tokens)295        return out_string296 297    def save_vocabulary(self, save_directory, filename_prefix: Optional[str] = None) -> Tuple[str]:298        """299        Save the vocabulary and special tokens file to a directory.300 301        Args:302            save_directory (`str`):303                The directory in which to save the vocabulary.304 305        Returns:306            `Tuple(str)`: Paths to the files saved.307        """308        if not os.path.isdir(save_directory):309            logger.error(f"Vocabulary path ({save_directory}) should be a directory")310            return311        out_vocab_file = os.path.join(312            save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"]313        )314 315        if os.path.abspath(self.vocab_file) != os.path.abspath(out_vocab_file) and os.path.isfile(self.vocab_file):316            copyfile(self.vocab_file, out_vocab_file)317        elif not os.path.isfile(self.vocab_file):318            with open(out_vocab_file, "wb") as fi:319                content_spiece_model = self.sp_model.serialized_model_proto()320                fi.write(content_spiece_model)321 322        return (out_vocab_file,)323 324    def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None):325        bos_token_id = [self.bos_token_id] if self.add_bos_token else []326        eos_token_id = [self.eos_token_id] if self.add_eos_token else []327 328        output = bos_token_id + token_ids_0 + eos_token_id329 330        if token_ids_1 is not None:331            output = output + bos_token_id + token_ids_1 + eos_token_id332 333        return output334 335    def get_special_tokens_mask(336        self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None, already_has_special_tokens: bool = False337    ) -> List[int]:338        """339        Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding340        special tokens using the tokenizer `prepare_for_model` method.341 342        Args:343            token_ids_0 (`List[int]`):344                List of IDs.345            token_ids_1 (`List[int]`, *optional*):346                Optional second list of IDs for sequence pairs.347            already_has_special_tokens (`bool`, *optional*, defaults to `False`):348                Whether or not the token list is already formatted with special tokens for the model.349 350        Returns:351            `List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.352        """353        if already_has_special_tokens:354            return super().get_special_tokens_mask(355                token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True356            )357 358        bos_token_id = [1] if self.add_bos_token else []359        eos_token_id = [1] if self.add_eos_token else []360 361        if token_ids_1 is None:362            return bos_token_id + ([0] * len(token_ids_0)) + eos_token_id363        return (364            bos_token_id365            + ([0] * len(token_ids_0))366            + eos_token_id367            + bos_token_id368            + ([0] * len(token_ids_1))369            + eos_token_id370        )371 372    def create_token_type_ids_from_sequences(373        self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None374    ) -> List[int]:375        """376        Creates a mask from the two sequences passed to be used in a sequence-pair classification task. An ALBERT377        sequence pair mask has the following format:378 379        ```380        0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1381        | first sequence    | second sequence |382        ```383 384        if token_ids_1 is None, only returns the first portion of the mask (0s).385 386        Args:387            token_ids_0 (`List[int]`):388                List of ids.389            token_ids_1 (`List[int]`, *optional*):390                Optional second list of IDs for sequence pairs.391 392        Returns:393            `List[int]`: List of [token type IDs](../glossary#token-type-ids) according to the given sequence(s).394        """395        bos_token_id = [self.bos_token_id] if self.add_bos_token else []396        eos_token_id = [self.eos_token_id] if self.add_eos_token else []397 398        output = [0] * len(bos_token_id + token_ids_0 + eos_token_id)399 400        if token_ids_1 is not None:401            output += [1] * len(bos_token_id + token_ids_1 + eos_token_id)402 403        return output404