CoolFace
Apppublic

DoruC/Grounded-Segment-Anything

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
tokenization_code_llama.py506 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."""18import os19from shutil import copyfile20from typing import Any, Dict, List, Optional, Tuple21 22import sentencepiece as spm23 24from ...convert_slow_tokenizer import import_protobuf25from ...tokenization_utils import AddedToken, PreTrainedTokenizer26from ...utils import logging, requires_backends27 28 29logger = logging.get_logger(__name__)30 31VOCAB_FILES_NAMES = {"vocab_file": "tokenizer.model"}32 33PRETRAINED_VOCAB_FILES_MAP = {34    "vocab_file": {35        "hf-internal-testing/llama-code-tokenizer": "https://huggingface.co/hf-internal-testing/llama-tokenizer/resolve/main/tokenizer.model",36    },37    "tokenizer_file": {38        "hf-internal-testing/llama-code-tokenizer": "https://huggingface.co/hf-internal-testing/llama-tokenizer/resolve/main/tokenizer_config.json",39    },40}41PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES = {42    "hf-internal-testing/llama-code-tokenizer": 2048,43}44SPIECE_UNDERLINE = "▁"45 46B_INST, E_INST = "[INST]", "[/INST]"47B_SYS, E_SYS = "<<SYS>>\n", "\n<</SYS>>\n\n"48 49# fmt: off50DEFAULT_SYSTEM_PROMPT = """You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your \51answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure\52 that your responses are socially unbiased and positive in nature.53 54If a question does not make any sense, or is not factually coherent, explain why instead of answering something not \55correct. If you don't know the answer to a question, please don't share false information."""56# fmt: on57 58 59class CodeLlamaTokenizer(PreTrainedTokenizer):60    """61    Construct a CodeLlama tokenizer. Based on byte-level Byte-Pair-Encoding. The default padding token is unset as62    there is no padding token in the original model.63 64    The default configuration match that of65    [codellama/CodeLlama-7b-Instruct-hf](https://huggingface.co/codellama/CodeLlama-7b-Instruct-hf/blob/main/tokenizer_config.json)66    which supports prompt infilling.67 68    Args:69        vocab_file (`str`):70            Path to the vocabulary file.71        eos_token (`str`, *optional*, defaults to `"</s>"`):72            The end of sequence token.73 74            <Tip>75 76            When building a sequence using special tokens, this is not the token that is used for the end of sequence.77            The token used is the `sep_token`.78 79            </Tip>80 81        unk_token (`str`, *optional*, defaults to `"<unk>"`):82            The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this83            token instead.84        prefix_token (`str`, *optional*, defaults to `"▁<PRE>"`):85            Prefix token used for infilling.86        suffix_token (`str`, *optional*, defaults to `"▁<SUF>"`):87            Suffix token used for infilling.88        middle_token (`str`, *optional*, defaults to `"▁<MID>"`):89            Middle token used for infilling.90        eot_token (`str`, *optional*, defaults to `"▁<EOT>"`):91            End of text token used for infilling.92        fill_token (`str`, *optional*, defaults to `"<FILL_ME>"`):93            The token used to split the input between the prefix and suffix.94        suffix_first (`bool`, *optional*, default to `False`):95            Whether the input prompt and suffix should be formatted with the suffix first.96        additional_special_tokens (`List[str]`, *optional*):97            Additional special tokens used by the tokenizer.98        sp_model_kwargs (`dict`, *optional*):99            Will be passed to the `SentencePieceProcessor.__init__()` method. The [Python wrapper for100            SentencePiece](https://github.com/google/sentencepiece/tree/master/python) can be used, among other things,101            to set:102 103            - `enable_sampling`: Enable subword regularization.104            - `nbest_size`: Sampling parameters for unigram. Invalid for BPE-Dropout.105 106              - `nbest_size = {0,1}`: No sampling is performed.107              - `nbest_size > 1`: samples from the nbest_size results.108              - `nbest_size < 0`: assuming that nbest_size is infinite and samples from the all hypothesis (lattice)109                using forward-filtering-and-backward-sampling algorithm.110 111            - `alpha`: Smoothing parameter for unigram sampling, and dropout probability of merge operations for112              BPE-dropout.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    pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP119    max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES120    model_input_names = ["input_ids", "attention_mask"]121 122    def __init__(123        self,124        vocab_file,125        unk_token="<unk>",126        bos_token="<s>",127        eos_token="</s>",128        prefix_token="▁<PRE>",129        middle_token="▁<MID>",130        suffix_token="▁<SUF>",131        eot_token="▁<EOT>",132        fill_token="<FILL_ME>",133        suffix_first=False,134        sp_model_kwargs: Optional[Dict[str, Any]] = None,135        add_bos_token=True,136        add_eos_token=False,137        clean_up_tokenization_spaces=False,138        additional_special_tokens=None,139        use_default_system_prompt=False,140        **kwargs,141    ):142        requires_backends(self, "protobuf")143        self.sp_model_kwargs = {} if sp_model_kwargs is None else sp_model_kwargs144        bos_token = AddedToken(bos_token, lstrip=False, rstrip=False) if isinstance(bos_token, str) else bos_token145        eos_token = AddedToken(eos_token, lstrip=False, rstrip=False) if isinstance(eos_token, str) else eos_token146        unk_token = AddedToken(unk_token, lstrip=False, rstrip=False) if isinstance(unk_token, str) else unk_token147 148        self.use_default_system_prompt = use_default_system_prompt149        # mark tokens special to skip them150        additional_special_tokens = additional_special_tokens or []151        for token in [prefix_token, middle_token, suffix_token, eot_token]:152            additional_special_tokens += [token] if token is not None else []153 154        self.vocab_file = vocab_file155        self.add_bos_token = add_bos_token156        self.add_eos_token = add_eos_token157        self._prefix_token = prefix_token158        self._middle_token = middle_token159        self._suffix_token = suffix_token160        self._eot_token = eot_token161        self.fill_token = fill_token162        self.suffix_first = suffix_first163        self.sp_model = self.get_spm_processor()164 165        super().__init__(166            bos_token=bos_token,167            eos_token=eos_token,168            unk_token=unk_token,169            add_bos_token=add_bos_token,170            add_eos_token=add_eos_token,171            prefix_token=prefix_token,172            middle_token=middle_token,173            suffix_token=suffix_token,174            eot_token=eot_token,175            fill_token=fill_token,176            sp_model_kwargs=self.sp_model_kwargs,177            suffix_first=suffix_first,178            clean_up_tokenization_spaces=clean_up_tokenization_spaces,179            additional_special_tokens=additional_special_tokens,180            use_default_system_prompt=use_default_system_prompt,181            **kwargs,182        )183 184    @property185    def unk_token_length(self):186        return len(self.sp_model.encode(str(self.unk_token)))187 188    def get_spm_processor(self):189        tokenizer = spm.SentencePieceProcessor(**self.sp_model_kwargs)190        with open(self.vocab_file, "rb") as f:191            sp_model = f.read()192            model_pb2 = import_protobuf()193            model = model_pb2.ModelProto.FromString(sp_model)194            normalizer_spec = model_pb2.NormalizerSpec()195            normalizer_spec.add_dummy_prefix = False196            model.normalizer_spec.MergeFrom(normalizer_spec)197            sp_model = model.SerializeToString()198            tokenizer.LoadFromSerializedProto(sp_model)199        return tokenizer200 201    @property202    def prefix_token(self):203        return self._prefix_token204 205    @property206    def prefix_id(self):207        if self._prefix_token is None:208            return None209        return self.convert_tokens_to_ids(self.prefix_token)210 211    @property212    def middle_token(self):213        return self._middle_token214 215    @property216    def middle_id(self):217        if self._middle_token is None:218            return None219        return self.convert_tokens_to_ids(self.middle_token)220 221    @property222    def suffix_token(self):223        return self._suffix_token224 225    @property226    def suffix_id(self):227        if self._suffix_token is None:228            return None229        return self.convert_tokens_to_ids(self.suffix_token)230 231    @property232    def eot_token(self):233        return self._eot_token234 235    @property236    def eot_id(self):237        if self._eot_token is None:238            return None239        return self.convert_tokens_to_ids(self.eot_token)240 241    @property242    def vocab_size(self):243        """Returns vocab size"""244        return self.sp_model.get_piece_size()245 246    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.get_vocab247    def get_vocab(self):248        """Returns vocab as a dict"""249        vocab = {self.convert_ids_to_tokens(i): i for i in range(self.vocab_size)}250        vocab.update(self.added_tokens_encoder)251        return vocab252 253    def tokenize(self, prefix, suffix=None, suffix_first=False, **kwargs) -> List[int]:254        # add a prefix space to `prefix`255        if self.fill_token is not None and self.fill_token in prefix and suffix is None:256            prefix, suffix = prefix.split(self.fill_token)257 258        if len(prefix) > 0:259            prefix = SPIECE_UNDERLINE + prefix.replace(SPIECE_UNDERLINE, " ")260 261        if suffix is None or len(suffix) < 1:262            tokens = super().tokenize(prefix, **kwargs)263            if len(tokens) > 1 and tokens[0] == SPIECE_UNDERLINE and tokens[1] in self.all_special_tokens:264                tokens = tokens[1:]265            return tokens266 267        prefix_tokens = self._tokenize(prefix)  # prefix has an extra `SPIECE_UNDERLINE`268 269        if None in (self.prefix_id, self.middle_id, self.suffix_id):270            raise ValueError(271                "The input either includes a `prefix` and a `suffix` used for the infilling task,"272                f"  or can be split on the {self.fill_token} token, creating a suffix and prefix,"273                " but the model does not support `infilling`."274            )275        suffix_tokens = self._tokenize(suffix)  # make sure CodeLlama sp model does not mess up276 277        suffix_first = suffix_first if suffix_first is not None else self.suffix_first278        if suffix_first:279            # format as " <PRE> <SUF>{suf} <MID> {pre}"280            return [self.prefix_token, self.suffix_token] + suffix_tokens + [self.middle_token] + prefix_tokens281        else:282            # format as " <PRE> {pre} <SUF>{suf} <MID>"283            return [self.prefix_token] + prefix_tokens + [self.suffix_token] + suffix_tokens + [self.middle_token]284 285    def _tokenize(self, text, **kwargs):286        """287        Returns a tokenized string.288 289        We de-activated the `add_dummy_prefix` option, thus the sentencepiece internals will always strip any290        SPIECE_UNDERLINE. For example: `self.sp_model.encode(f"{SPIECE_UNDERLINE}Hey", out_type = str)` will give291        `['H', 'e', 'y']` instead of `['▁He', 'y']`. Thus we always encode `f"{unk_token}text"` and strip the292        `unk_token`. Here is an example with `unk_token = "<unk>"` and `unk_token_length = 4`.293        `self.tokenizer.sp_model.encode("<unk> Hey", out_type = str)[4:]`.294        """295        tokens = self.sp_model.encode(text, out_type=str)296        if not text.startswith((SPIECE_UNDERLINE, " ")):297            return tokens298        # 1. Encode string + prefix ex: "<unk> Hey"299        tokens = self.sp_model.encode(self.unk_token + text, out_type=str)300        # 2. Remove self.unk_token from ['<','unk','>', '▁Hey']301        return tokens[self.unk_token_length :] if len(tokens) >= self.unk_token_length else tokens302 303    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer._convert_token_to_id304    def _convert_token_to_id(self, token):305        """Converts a token (str) in an id using the vocab."""306        return self.sp_model.piece_to_id(token)307 308    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer._convert_id_to_token309    def _convert_id_to_token(self, index):310        """Converts an index (integer) in a token (str) using the vocab."""311        token = self.sp_model.IdToPiece(index)312        return token313 314    def convert_tokens_to_string(self, tokens):315        """Converts a sequence of tokens (string) in a single string."""316        # since we manually add the prefix space, we have to remove it when decoding317        if tokens[0].startswith(SPIECE_UNDERLINE):318            tokens[0] = tokens[0][1:]319 320        current_sub_tokens = []321        out_string = ""322        for _, token in enumerate(tokens):323            # make sure that special tokens are not decoded using sentencepiece model324            if token in self.all_special_tokens:325                out_string += self.sp_model.decode(current_sub_tokens) + token326                current_sub_tokens = []327            else:328                current_sub_tokens.append(token)329        out_string += self.sp_model.decode(current_sub_tokens)330        return out_string331 332    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.save_vocabulary333    def save_vocabulary(self, save_directory, filename_prefix: Optional[str] = None) -> Tuple[str]:334        """335        Save the vocabulary and special tokens file to a directory.336 337        Args:338            save_directory (`str`):339                The directory in which to save the vocabulary.340 341        Returns:342            `Tuple(str)`: Paths to the files saved.343        """344        if not os.path.isdir(save_directory):345            logger.error(f"Vocabulary path ({save_directory}) should be a directory")346            return347        out_vocab_file = os.path.join(348            save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"]349        )350 351        if os.path.abspath(self.vocab_file) != os.path.abspath(out_vocab_file) and os.path.isfile(self.vocab_file):352            copyfile(self.vocab_file, out_vocab_file)353        elif not os.path.isfile(self.vocab_file):354            with open(out_vocab_file, "wb") as fi:355                content_spiece_model = self.sp_model.serialized_model_proto()356                fi.write(content_spiece_model)357 358        return (out_vocab_file,)359 360    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.build_inputs_with_special_tokens361    def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None):362        bos_token_id = [self.bos_token_id] if self.add_bos_token else []363        eos_token_id = [self.eos_token_id] if self.add_eos_token else []364 365        output = bos_token_id + token_ids_0 + eos_token_id366 367        if token_ids_1 is not None:368            output = output + bos_token_id + token_ids_1 + eos_token_id369 370        return output371 372    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.get_special_tokens_mask373    def get_special_tokens_mask(374        self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None, already_has_special_tokens: bool = False375    ) -> List[int]:376        """377        Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding378        special tokens using the tokenizer `prepare_for_model` method.379 380        Args:381            token_ids_0 (`List[int]`):382                List of IDs.383            token_ids_1 (`List[int]`, *optional*):384                Optional second list of IDs for sequence pairs.385            already_has_special_tokens (`bool`, *optional*, defaults to `False`):386                Whether or not the token list is already formatted with special tokens for the model.387 388        Returns:389            `List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.390        """391        if already_has_special_tokens:392            return super().get_special_tokens_mask(393                token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True394            )395 396        bos_token_id = [1] if self.add_bos_token else []397        eos_token_id = [1] if self.add_eos_token else []398 399        if token_ids_1 is None:400            return bos_token_id + ([0] * len(token_ids_0)) + eos_token_id401        return (402            bos_token_id403            + ([0] * len(token_ids_0))404            + eos_token_id405            + bos_token_id406            + ([0] * len(token_ids_1))407            + eos_token_id408        )409 410    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.create_token_type_ids_from_sequences411    def create_token_type_ids_from_sequences(412        self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None413    ) -> List[int]:414        """415        Creates a mask from the two sequences passed to be used in a sequence-pair classification task. An ALBERT416        sequence pair mask has the following format:417 418        ```419        0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1420        | first sequence    | second sequence |421        ```422 423        if token_ids_1 is None, only returns the first portion of the mask (0s).424 425        Args:426            token_ids_0 (`List[int]`):427                List of ids.428            token_ids_1 (`List[int]`, *optional*):429                Optional second list of IDs for sequence pairs.430 431        Returns:432            `List[int]`: List of [token type IDs](../glossary#token-type-ids) according to the given sequence(s).433        """434        bos_token_id = [self.bos_token_id] if self.add_bos_token else []435        eos_token_id = [self.eos_token_id] if self.add_eos_token else []436 437        output = [0] * len(bos_token_id + token_ids_0 + eos_token_id)438 439        if token_ids_1 is not None:440            output += [1] * len(bos_token_id + token_ids_1 + eos_token_id)441 442        return output443 444    @property445    # Copied from transformers.models.llama.tokenization_llama.LlamaTokenizer.default_chat_template446    def default_chat_template(self):447        """448        LLaMA uses [INST] and [/INST] to indicate user messages, and <<SYS>> and <</SYS>> to indicate system messages.449        Assistant messages do not have special tokens, because LLaMA chat models are generally trained with strict450        user/assistant/user/assistant message ordering, and so assistant messages can be identified from the ordering451        rather than needing special tokens. The system message is partly 'embedded' in the first user message, which452        results in an unusual token ordering when it is present. This template should definitely be changed if you wish453        to fine-tune a model with more flexible role ordering!454 455        The output should look something like:456 457        <bos>[INST] B_SYS SystemPrompt E_SYS Prompt [/INST] Answer <eos> <bos>[INST] Prompt [/INST] Answer <eos>458        <bos>[INST] Prompt [/INST]459        """460 461        template = (462            "{% if messages[0]['role'] == 'system' %}"463            "{% set loop_messages = messages[1:] %}"  # Extract system message if it's present464            "{% set system_message = messages[0]['content'] %}"465            "{% elif USE_DEFAULT_PROMPT == true and not '<<SYS>>' in messages[0]['content'] %}"466            "{% set loop_messages = messages %}"  # Or use the default system message if the flag is set467            "{% set system_message = 'DEFAULT_SYSTEM_MESSAGE' %}"468            "{% else %}"469            "{% set loop_messages = messages %}"470            "{% set system_message = false %}"471            "{% endif %}"472            "{% for message in loop_messages %}"  # Loop over all non-system messages473            "{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}"474            "{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}"475            "{% endif %}"476            "{% if loop.index0 == 0 and system_message != false %}"  # Embed system message in first message477            "{% set content = '<<SYS>>\\n' + system_message + '\\n<</SYS>>\\n\\n' + message['content'] %}"478            "{% else %}"479            "{% set content = message['content'] %}"480            "{% endif %}"481            "{% if message['role'] == 'user' %}"  # After all of that, handle messages/roles in a fairly normal way482            "{{ bos_token + '[INST] ' + content.strip() + ' [/INST]' }}"483            "{% elif message['role'] == 'system' %}"484            "{{ '<<SYS>>\\n' + content.strip() + '\\n<</SYS>>\\n\\n' }}"485            "{% elif message['role'] == 'assistant' %}"486            "{{ ' '  + content.strip() + ' ' + eos_token }}"487            "{% endif %}"488            "{% endfor %}"489        )490        template = template.replace("USE_DEFAULT_PROMPT", "true" if self.use_default_system_prompt else "false")491        default_message = DEFAULT_SYSTEM_PROMPT.replace("\n", "\\n").replace("'", "\\'")492        template = template.replace("DEFAULT_SYSTEM_MESSAGE", default_message)493 494        return template495 496    def __getstate__(self):497        state = self.__dict__.copy()498        state["sp_model"] = None499        state["sp_model_proto"] = self.sp_model.serialized_model_proto()500        return state501 502    def __setstate__(self, d):503        self.__dict__ = d504        self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)505        self.sp_model.LoadFromSerializedProto(self.sp_model_proto)506