CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
tokenization_code_llama.py455 linesDownload Raw Back to code_llama
1# coding=utf-82# Copyright 2023 MetaAI and the HuggingFace Inc. team. All rights reserved.3#4#5# Licensed under the Apache License, Version 2.0 (the "License");6# you may not use this file except in compliance with the License.7# You may obtain a copy of the License at8#9#     http://www.apache.org/licenses/LICENSE-2.010#11# Unless required by applicable law or agreed to in writing, software12# distributed under the License is distributed on an "AS IS" BASIS,13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14# See the License for the specific language governing permissions and15# limitations under the License.16 17"""Tokenization classes for Code LLaMA."""18 19import os20from shutil import copyfile21from typing import Any, Optional22 23import sentencepiece as spm24 25from ...convert_slow_tokenizer import import_protobuf26from ...tokenization_utils import AddedToken, PreTrainedTokenizer27from ...utils import logging, requires_backends28from ...utils.import_utils import requires29 30 31logger = logging.get_logger(__name__)32 33VOCAB_FILES_NAMES = {"vocab_file": "tokenizer.model"}34 35SPIECE_UNDERLINE = "▁"36 37B_INST, E_INST = "[INST]", "[/INST]"38B_SYS, E_SYS = "<<SYS>>\n", "\n<</SYS>>\n\n"39 40# fmt: off41DEFAULT_SYSTEM_PROMPT = """You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your \42answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure\43 that your responses are socially unbiased and positive in nature.44 45If a question does not make any sense, or is not factually coherent, explain why instead of answering something not \46correct. If you don't know the answer to a question, please don't share false information."""47# fmt: on48 49 50@requires(backends=("sentencepiece",))51class CodeLlamaTokenizer(PreTrainedTokenizer):52    """53    Construct a CodeLlama tokenizer. Based on byte-level Byte-Pair-Encoding. The default padding token is unset as54    there is no padding token in the original model.55 56    The default configuration match that of57    [codellama/CodeLlama-7b-Instruct-hf](https://huggingface.co/meta-llama/CodeLlama-7b-Instruct-hf/blob/main/tokenizer_config.json)58    which supports prompt infilling.59 60    Args:61        vocab_file (`str`):62            Path to the vocabulary file.63        unk_token (`str`, *optional*, defaults to `"<unk>"`):64            The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this65            token instead.66        bos_token (`str`, *optional*, defaults to `"<s>"`):67            The beginning of sequence token that was used during pretraining. Can be used a sequence classifier token.68        eos_token (`str`, *optional*, defaults to `"</s>"`):69            The end of sequence token.70 71            <Tip>72 73            When building a sequence using special tokens, this is not the token that is used for the end of sequence.74            The token used is the `sep_token`.75 76            </Tip>77 78        prefix_token (`str`, *optional*, defaults to `"▁<PRE>"`):79            Prefix token used for infilling.80        middle_token (`str`, *optional*, defaults to `"▁<MID>"`):81            Middle token used for infilling.82        suffix_token (`str`, *optional*, defaults to `"▁<SUF>"`):83            Suffix token used for infilling.84        eot_token (`str`, *optional*, defaults to `"▁<EOT>"`):85            End of text token used for infilling.86        fill_token (`str`, *optional*, defaults to `"<FILL_ME>"`):87            The token used to split the input between the prefix and suffix.88        suffix_first (`bool`, *optional*, defaults to `False`):89            Whether the input prompt and suffix should be formatted with the suffix first.90        sp_model_kwargs (`dict`, *optional*):91            Will be passed to the `SentencePieceProcessor.__init__()` method. The [Python wrapper for92            SentencePiece](https://github.com/google/sentencepiece/tree/master/python) can be used, among other things,93            to set:94 95            - `enable_sampling`: Enable subword regularization.96            - `nbest_size`: Sampling parameters for unigram. Invalid for BPE-Dropout.97 98              - `nbest_size = {0,1}`: No sampling is performed.99              - `nbest_size > 1`: samples from the nbest_size results.100              - `nbest_size < 0`: assuming that nbest_size is infinite and samples from the all hypothesis (lattice)101                using forward-filtering-and-backward-sampling algorithm.102 103            - `alpha`: Smoothing parameter for unigram sampling, and dropout probability of merge operations for104              BPE-dropout.105        add_bos_token (`bool`, *optional*, defaults to `True`):106            Whether to add a beginning of sequence token at the start of sequences.107        add_eos_token (`bool`, *optional*, defaults to `False`):108            Whether to add an end of sequence token at the end of sequences.109        clean_up_tokenization_spaces (`bool`, *optional*, defaults to `False`):110            Whether or not to clean up the tokenization spaces.111        additional_special_tokens (`list[str]`, *optional*):112            Additional special tokens used by the tokenizer.113        use_default_system_prompt (`bool`, *optional*, defaults to `False`):114            Whether or not the default system prompt for Llama should be used.115    """116 117    vocab_files_names = VOCAB_FILES_NAMES118    model_input_names = ["input_ids", "attention_mask"]119 120    def __init__(121        self,122        vocab_file,123        unk_token="<unk>",124        bos_token="<s>",125        eos_token="</s>",126        prefix_token="▁<PRE>",127        middle_token="▁<MID>",128        suffix_token="▁<SUF>",129        eot_token="▁<EOT>",130        fill_token="<FILL_ME>",131        suffix_first=False,132        sp_model_kwargs: Optional[dict[str, Any]] = None,133        add_bos_token=True,134        add_eos_token=False,135        clean_up_tokenization_spaces=False,136        additional_special_tokens=None,137        use_default_system_prompt=False,138        **kwargs,139    ):140        requires_backends(self, "protobuf")141        self.sp_model_kwargs = {} if sp_model_kwargs is None else sp_model_kwargs142        bos_token = AddedToken(bos_token, normalized=False, special=True) if isinstance(bos_token, str) else bos_token143        eos_token = AddedToken(eos_token, normalized=False, special=True) if isinstance(eos_token, str) else eos_token144        unk_token = AddedToken(unk_token, normalized=False, special=True) if isinstance(unk_token, str) else unk_token145 146        self.use_default_system_prompt = use_default_system_prompt147        # mark tokens special to skip them148        additional_special_tokens = additional_special_tokens or []149        for token in [prefix_token, middle_token, suffix_token, eot_token]:150            additional_special_tokens += [token] if token is not None else []151 152        self.vocab_file = vocab_file153        self.add_bos_token = add_bos_token154        self.add_eos_token = add_eos_token155        self._prefix_token = prefix_token156        self._middle_token = middle_token157        self._suffix_token = suffix_token158        self._eot_token = eot_token159        self.fill_token = fill_token160        self.suffix_first = suffix_first161        self.sp_model = self.get_spm_processor()162 163        super().__init__(164            bos_token=bos_token,165            eos_token=eos_token,166            unk_token=unk_token,167            add_bos_token=add_bos_token,168            add_eos_token=add_eos_token,169            prefix_token=prefix_token,170            middle_token=middle_token,171            suffix_token=suffix_token,172            eot_token=eot_token,173            fill_token=fill_token,174            sp_model_kwargs=self.sp_model_kwargs,175            suffix_first=suffix_first,176            clean_up_tokenization_spaces=clean_up_tokenization_spaces,177            additional_special_tokens=additional_special_tokens,178            use_default_system_prompt=use_default_system_prompt,179            **kwargs,180        )181 182    @property183    def unk_token_length(self):184        return len(self.sp_model.encode(str(self.unk_token)))185 186    def get_spm_processor(self):187        tokenizer = spm.SentencePieceProcessor(**self.sp_model_kwargs)188        with open(self.vocab_file, "rb") as f:189            sp_model = f.read()190            model_pb2 = import_protobuf()191            model = model_pb2.ModelProto.FromString(sp_model)192            normalizer_spec = model_pb2.NormalizerSpec()193            normalizer_spec.add_dummy_prefix = False194            model.normalizer_spec.MergeFrom(normalizer_spec)195            sp_model = model.SerializeToString()196            tokenizer.LoadFromSerializedProto(sp_model)197        return tokenizer198 199    @property200    def prefix_token(self):201        return self._prefix_token202 203    @property204    def prefix_id(self):205        if self._prefix_token is None:206            return None207        return self.convert_tokens_to_ids(self.prefix_token)208 209    @property210    def middle_token(self):211        return self._middle_token212 213    @property214    def middle_id(self):215        if self._middle_token is None:216            return None217        return self.convert_tokens_to_ids(self.middle_token)218 219    @property220    def suffix_token(self):221        return self._suffix_token222 223    @property224    def suffix_id(self):225        if self._suffix_token is None:226            return None227        return self.convert_tokens_to_ids(self.suffix_token)228 229    @property230    def eot_token(self):231        return self._eot_token232 233    @property234    def eot_id(self):235        if self._eot_token is None:236            return None237        return self.convert_tokens_to_ids(self.eot_token)238 239    @property240    def vocab_size(self):241        """Returns vocab size"""242        return self.sp_model.get_piece_size()243 244    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.get_vocab245    def get_vocab(self):246        """Returns vocab as a dict"""247        vocab = {self.convert_ids_to_tokens(i): i for i in range(self.vocab_size)}248        vocab.update(self.added_tokens_encoder)249        return vocab250 251    def tokenize(self, prefix, suffix=None, suffix_first=False, **kwargs) -> list[int]:252        # add a prefix space to `prefix`253        if self.fill_token is not None and self.fill_token in prefix and suffix is None:254            prefix, suffix = prefix.split(self.fill_token)255 256        if len(prefix) > 0:257            prefix = SPIECE_UNDERLINE + prefix.replace(SPIECE_UNDERLINE, " ")258 259        if suffix is None or len(suffix) < 1:260            tokens = super().tokenize(prefix, **kwargs)261            if len(tokens) > 1 and tokens[0] == SPIECE_UNDERLINE and tokens[1] in self.all_special_tokens:262                tokens = tokens[1:]263            return tokens264 265        prefix_tokens = self._tokenize(prefix)  # prefix has an extra `SPIECE_UNDERLINE`266 267        if None in (self.prefix_id, self.middle_id, self.suffix_id):268            raise ValueError(269                "The input either includes a `prefix` and a `suffix` used for the infilling task,"270                f"  or can be split on the {self.fill_token} token, creating a suffix and prefix,"271                " but the model does not support `infilling`."272            )273        suffix_tokens = self._tokenize(suffix)  # make sure CodeLlama sp model does not mess up274 275        suffix_first = suffix_first if suffix_first is not None else self.suffix_first276        if suffix_first:277            # format as " <PRE> <SUF>{suf} <MID> {pre}"278            return [self.prefix_token, self.suffix_token] + suffix_tokens + [self.middle_token] + prefix_tokens279        else:280            # format as " <PRE> {pre} <SUF>{suf} <MID>"281            return [self.prefix_token] + prefix_tokens + [self.suffix_token] + suffix_tokens + [self.middle_token]282 283    def _tokenize(self, text, **kwargs):284        """285        Returns a tokenized string.286 287        We de-activated the `add_dummy_prefix` option, thus the sentencepiece internals will always strip any288        SPIECE_UNDERLINE. For example: `self.sp_model.encode(f"{SPIECE_UNDERLINE}Hey", out_type = str)` will give289        `['H', 'e', 'y']` instead of `['▁He', 'y']`. Thus we always encode `f"{unk_token}text"` and strip the290        `unk_token`. Here is an example with `unk_token = "<unk>"` and `unk_token_length = 4`.291        `self.tokenizer.sp_model.encode("<unk> Hey", out_type = str)[4:]`.292        """293        tokens = self.sp_model.encode(text, out_type=str)294        if not text.startswith((SPIECE_UNDERLINE, " ")):295            return tokens296        # 1. Encode string + prefix ex: "<unk> Hey"297        tokens = self.sp_model.encode(self.unk_token + text, out_type=str)298        # 2. Remove self.unk_token from ['<','unk','>', '▁Hey']299        return tokens[self.unk_token_length :] if len(tokens) >= self.unk_token_length else tokens300 301    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer._convert_token_to_id302    def _convert_token_to_id(self, token):303        """Converts a token (str) in an id using the vocab."""304        return self.sp_model.piece_to_id(token)305 306    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer._convert_id_to_token307    def _convert_id_to_token(self, index):308        """Converts an index (integer) in a token (str) using the vocab."""309        token = self.sp_model.IdToPiece(index)310        return token311 312    def convert_tokens_to_string(self, tokens):313        """Converts a sequence of tokens (string) in a single string."""314        # since we manually add the prefix space, we have to remove it when decoding315        if tokens[0].startswith(SPIECE_UNDERLINE):316            tokens[0] = tokens[0][1:]317 318        current_sub_tokens = []319        out_string = ""320        for _, token in enumerate(tokens):321            # make sure that special tokens are not decoded using sentencepiece model322            if token in self.all_special_tokens:323                out_string += self.sp_model.decode(current_sub_tokens) + token324                current_sub_tokens = []325            else:326                current_sub_tokens.append(token)327        out_string += self.sp_model.decode(current_sub_tokens)328        return out_string329 330    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.save_vocabulary331    def save_vocabulary(self, save_directory, filename_prefix: Optional[str] = None) -> tuple[str]:332        """333        Save the vocabulary and special tokens file to a directory.334 335        Args:336            save_directory (`str`):337                The directory in which to save the vocabulary.338 339        Returns:340            `Tuple(str)`: Paths to the files saved.341        """342        if not os.path.isdir(save_directory):343            logger.error(f"Vocabulary path ({save_directory}) should be a directory")344            return345        out_vocab_file = os.path.join(346            save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"]347        )348 349        if os.path.abspath(self.vocab_file) != os.path.abspath(out_vocab_file) and os.path.isfile(self.vocab_file):350            copyfile(self.vocab_file, out_vocab_file)351        elif not os.path.isfile(self.vocab_file):352            with open(out_vocab_file, "wb") as fi:353                content_spiece_model = self.sp_model.serialized_model_proto()354                fi.write(content_spiece_model)355 356        return (out_vocab_file,)357 358    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.build_inputs_with_special_tokens359    def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None):360        bos_token_id = [self.bos_token_id] if self.add_bos_token else []361        eos_token_id = [self.eos_token_id] if self.add_eos_token else []362 363        output = bos_token_id + token_ids_0 + eos_token_id364 365        if token_ids_1 is not None:366            output = output + bos_token_id + token_ids_1 + eos_token_id367 368        return output369 370    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.get_special_tokens_mask371    def get_special_tokens_mask(372        self, token_ids_0: list[int], token_ids_1: Optional[list[int]] = None, already_has_special_tokens: bool = False373    ) -> list[int]:374        """375        Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding376        special tokens using the tokenizer `prepare_for_model` method.377 378        Args:379            token_ids_0 (`list[int]`):380                List of IDs.381            token_ids_1 (`list[int]`, *optional*):382                Optional second list of IDs for sequence pairs.383            already_has_special_tokens (`bool`, *optional*, defaults to `False`):384                Whether or not the token list is already formatted with special tokens for the model.385 386        Returns:387            `list[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.388        """389        if already_has_special_tokens:390            return super().get_special_tokens_mask(391                token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True392            )393 394        bos_token_id = [1] if self.add_bos_token else []395        eos_token_id = [1] if self.add_eos_token else []396 397        if token_ids_1 is None:398            return bos_token_id + ([0] * len(token_ids_0)) + eos_token_id399        return (400            bos_token_id401            + ([0] * len(token_ids_0))402            + eos_token_id403            + bos_token_id404            + ([0] * len(token_ids_1))405            + eos_token_id406        )407 408    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.create_token_type_ids_from_sequences409    def create_token_type_ids_from_sequences(410        self, token_ids_0: list[int], token_ids_1: Optional[list[int]] = None411    ) -> list[int]:412        """413        Creates a mask from the two sequences passed to be used in a sequence-pair classification task. An ALBERT414        sequence pair mask has the following format:415 416        ```417        0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1418        | first sequence    | second sequence |419        ```420 421        if token_ids_1 is None, only returns the first portion of the mask (0s).422 423        Args:424            token_ids_0 (`list[int]`):425                List of ids.426            token_ids_1 (`list[int]`, *optional*):427                Optional second list of IDs for sequence pairs.428 429        Returns:430            `list[int]`: List of [token type IDs](../glossary#token-type-ids) according to the given sequence(s).431        """432        bos_token_id = [self.bos_token_id] if self.add_bos_token else []433        eos_token_id = [self.eos_token_id] if self.add_eos_token else []434 435        output = [0] * len(bos_token_id + token_ids_0 + eos_token_id)436 437        if token_ids_1 is not None:438            output += [1] * len(bos_token_id + token_ids_1 + eos_token_id)439 440        return output441 442    def __getstate__(self):443        state = self.__dict__.copy()444        state["sp_model"] = None445        state["sp_model_proto"] = self.sp_model.serialized_model_proto()446        return state447 448    def __setstate__(self, d):449        self.__dict__ = d450        self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)451        self.sp_model.LoadFromSerializedProto(self.sp_model_proto)452 453 454__all__ = ["CodeLlamaTokenizer"]455 
Aluode/PerceptionLabPortable · CoolFace