CoolFace
Modelpublic

Multilingual-Multimodal-NLP/LoopCoder-V2

sourceHugging Faceapache-2.0updated 3mo agoView on Hugging Face
49likes182downloads
tokenization_iquestcoder.py571 linesDownload Raw Back to root
1"""Tokenization classes for IQuestCoder."""2 3import os4from shutil import copyfile5from typing import Any, Dict, List, Optional, Tuple, Union6 7import sentencepiece as spm8 9from transformers.tokenization_utils import AddedToken, PreTrainedTokenizer10from transformers.utils import logging11 12 13logger = logging.get_logger(__name__)14 15VOCAB_FILES_NAMES = {"vocab_file": "tokenizer.model"}16 17PRETRAINED_VOCAB_FILES_MAP = {18    "vocab_file": {},19    "tokenizer_file": {},20}21PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES = {}22 23 24 25class IQuestCoderTokenizer(PreTrainedTokenizer):26 27    vocab_files_names = VOCAB_FILES_NAMES28    pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP29    max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES30    model_input_names = ["input_ids", "attention_mask"]31 32    def __init__(33        self,34        vocab_file,35        unk_token="<unk>",36        bos_token="<s>",37        eos_token="</s>",38        pad_token=None,39        sp_model_kwargs: Optional[Dict[str, Any]] = None,40        add_bos_token=True,41        add_eos_token=False,42        clean_up_tokenization_spaces=False,43        add_prefix_space=False,44        legacy=None,45        use_default_system_prompt=False,46        chat_template=None,47        **kwargs,48    ):49        self.sp_model_kwargs = {} if sp_model_kwargs is None else sp_model_kwargs50        bos_token = AddedToken(bos_token, lstrip=False, rstrip=False) if isinstance(bos_token, str) else bos_token51        eos_token = AddedToken(eos_token, lstrip=False, rstrip=False) if isinstance(eos_token, str) else eos_token52        unk_token = AddedToken(unk_token, lstrip=False, rstrip=False) if isinstance(unk_token, str) else unk_token53        pad_token = AddedToken(pad_token, lstrip=False, rstrip=False) if isinstance(pad_token, str) else pad_token54        55        # Legacy behavior handling56        if legacy is None:57            logger.warning_once(58                f"You are using the default legacy behaviour of the {self.__class__.__name__}. This is"59                " expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you."60                " If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it"61                " means, and thoroughly read the reason why this was added as explained in"62                " https://github.com/huggingface/transformers/pull/24565"63            )64            legacy = True65        66        self.legacy = legacy67        self.vocab_file = vocab_file68        self.add_bos_token = add_bos_token69        self.add_eos_token = add_eos_token70        self.add_prefix_space = add_prefix_space71        self.use_default_system_prompt = use_default_system_prompt72        self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)73        self.sp_model.Load(vocab_file)74        75 76        77        super().__init__(78            bos_token=bos_token,79            eos_token=eos_token,80            unk_token=unk_token,81            pad_token=pad_token,82            add_bos_token=add_bos_token,83            add_eos_token=add_eos_token,84            sp_model_kwargs=self.sp_model_kwargs,85            clean_up_tokenization_spaces=clean_up_tokenization_spaces,86            add_prefix_space=add_prefix_space,87            legacy=legacy,88            use_default_system_prompt=use_default_system_prompt,89            chat_template=chat_template,90            **kwargs,91        )92 93    def __getstate__(self):94        state = self.__dict__.copy()95        state["sp_model"] = None96        return state97 98    def __setstate__(self, d):99        self.__dict__ = d100        self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)101        self.sp_model.Load(self.vocab_file)102 103    @property104    def vocab_size(self) -> int:105        """Returns the vocabulary size."""106        return self.sp_model.get_piece_size()107 108    def get_vocab(self) -> Dict[str, int]:109        """Returns the vocabulary as a dictionary of token to index."""110        vocab = {self.convert_ids_to_tokens(i): i for i in range(self.vocab_size)}111        vocab.update(self.added_tokens_encoder)112        return vocab113 114    def _tokenize(self, text: str) -> List[str]:115        """116        Tokenize a string.117        118        Args:119            text (`str`): The text to tokenize.120            121        Returns:122            `List[str]`: The list of tokens.123        """124        if self.add_prefix_space:125            text = " " + text126            127        if self.legacy:128            return self.sp_model.encode(text, out_type=str)129        130        # Non-legacy behavior: handle special tokens properly131        return self.sp_model.encode(text, out_type=str)132 133    def _convert_token_to_id(self, token: str) -> int:134        """Converts a token (str) to an id using the vocab."""135        return self.sp_model.piece_to_id(token)136 137    def _convert_id_to_token(self, index: int) -> str:138        """Converts an index (integer) to a token (str) using the vocab."""139        token = self.sp_model.IdToPiece(index)140        return token141 142    def convert_tokens_to_string(self, tokens: List[str]) -> str:143        """144        Converts a sequence of tokens (strings) to a single string.145        146        This method handles special tokens separately to ensure they are not147        decoded using the SentencePiece model.148        149        Args:150            tokens (`List[str]`): The list of tokens to convert.151            152        Returns:153            `str`: The decoded string.154        """155        current_sub_tokens = []156        out_string = ""157        prev_is_special = False158        for i, token in enumerate(tokens):159            # make sure that special tokens are not decoded using sentencepiece model160            if token in self.all_special_tokens:161                if not prev_is_special and i != 0:162                    out_string += " "163                out_string += self.sp_model.decode(current_sub_tokens) + token164                prev_is_special = True165                current_sub_tokens = []166            else:167                current_sub_tokens.append(token)168                prev_is_special = False169        out_string += self.sp_model.decode(current_sub_tokens)170        return out_string171 172    def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None) -> Tuple[str]:173        """174        Save the vocabulary and special tokens file to a directory.175        176        Args:177            save_directory (`str`):178                The directory in which to save the vocabulary.179            filename_prefix (`str`, *optional*):180                An optional prefix to add to the named of the saved files.181                182        Returns:183            `Tuple(str)`: Paths to the files saved.184        """185        if not os.path.isdir(save_directory):186            logger.error(f"Vocabulary path ({save_directory}) should be a directory")187            return188        out_vocab_file = os.path.join(189            save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"]190        )191 192        if os.path.abspath(self.vocab_file) != os.path.abspath(out_vocab_file) and os.path.isfile(self.vocab_file):193            copyfile(self.vocab_file, out_vocab_file)194        elif not os.path.isfile(self.vocab_file):195            with open(out_vocab_file, "wb") as fi:196                content_spiece_model = self.sp_model.serialized_model_proto()197                fi.write(content_spiece_model)198 199        return (out_vocab_file,)200 201    def build_inputs_with_special_tokens(202        self, 203        token_ids_0: List[int], 204        token_ids_1: Optional[List[int]] = None205    ) -> List[int]:206        """207        Build model inputs from a sequence or a pair of sequences for sequence classification tasks by concatenating208        and adding special tokens.209        210        An IQuestCoder sequence has the following format:211        212        - single sequence: `<s> X </s>` (if add_eos_token is True) or `<s> X` (default)213        - pair of sequences: `<s> A </s> <s> B </s>` (if add_eos_token is True) or `<s> A <s> B` (default)214        215        Args:216            token_ids_0 (`List[int]`):217                List of IDs to which the special tokens will be added.218            token_ids_1 (`List[int]`, *optional*):219                Optional second list of IDs for sequence pairs.220                221        Returns:222            `List[int]`: List of input IDs with the appropriate special tokens.223        """224        bos_token_id = [self.bos_token_id] if self.add_bos_token else []225        eos_token_id = [self.eos_token_id] if self.add_eos_token else []226 227        output = bos_token_id + token_ids_0 + eos_token_id228 229        if token_ids_1 is not None:230            output = output + bos_token_id + token_ids_1 + eos_token_id231 232        return output233 234    def get_special_tokens_mask(235        self, 236        token_ids_0: List[int], 237        token_ids_1: Optional[List[int]] = None, 238        already_has_special_tokens: bool = False239    ) -> List[int]:240        """241        Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding242        special tokens using the tokenizer `prepare_for_model` method.243        244        Args:245            token_ids_0 (`List[int]`):246                List of IDs.247            token_ids_1 (`List[int]`, *optional*):248                Optional second list of IDs for sequence pairs.249            already_has_special_tokens (`bool`, *optional*, defaults to `False`):250                Whether or not the token list is already formatted with special tokens for the model.251                252        Returns:253            `List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.254        """255        if already_has_special_tokens:256            return super().get_special_tokens_mask(257                token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True258            )259 260        bos_token_id = [1] if self.add_bos_token else []261        eos_token_id = [1] if self.add_eos_token else []262 263        if token_ids_1 is None:264            return bos_token_id + ([0] * len(token_ids_0)) + eos_token_id265        return (266            bos_token_id267            + ([0] * len(token_ids_0))268            + eos_token_id269            + bos_token_id270            + ([0] * len(token_ids_1))271            + eos_token_id272        )273 274    def create_token_type_ids_from_sequences(275        self, 276        token_ids_0: List[int], 277        token_ids_1: Optional[List[int]] = None278    ) -> List[int]:279        """280        Create a mask from the two sequences passed to be used in a sequence-pair classification task.281        282        An IQuestCoder sequence pair mask has the following format:283        284        ```285        0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1286        | first sequence    | second sequence |287        ```288        289        If `token_ids_1` is `None`, this method only returns the first portion of the mask (0s).290        291        Args:292            token_ids_0 (`List[int]`):293                List of IDs.294            token_ids_1 (`List[int]`, *optional*):295                Optional second list of IDs for sequence pairs.296                297        Returns:298            `List[int]`: List of token type IDs according to the given sequence(s).299        """300        bos_token_id = [self.bos_token_id] if self.add_bos_token else []301        eos_token_id = [self.eos_token_id] if self.add_eos_token else []302 303        output = [0] * len(bos_token_id + token_ids_0 + eos_token_id)304 305        if token_ids_1 is not None:306            output += [1] * len(bos_token_id + token_ids_1 + eos_token_id)307 308        return output309 310    @property311    def default_chat_template(self) -> str:312        """313        Returns the default chat template for IQuestCoder.314        315        This template formats conversations with system, user, and assistant roles.316        """317        return DEFAULT_CHAT_TEMPLATE318 319    def apply_chat_template(320        self,321        conversation: Union[List[Dict[str, str]], "Conversation"],322        chat_template: Optional[str] = None,323        add_generation_prompt: bool = False,324        tokenize: bool = True,325        padding: bool = False,326        truncation: bool = False,327        max_length: Optional[int] = None,328        return_tensors: Optional[str] = None,329        return_dict: bool = False,330        **tokenizer_kwargs,331    ):332        """333        Apply a chat template to format a conversation.334        335        Args:336            conversation (`List[Dict[str, str]]` or `Conversation`):337                A list of dicts with "role" and "content" keys, representing the conversation history.338            chat_template (`str`, *optional*):339                A Jinja template to use for formatting. If not provided, the tokenizer's default will be used.340            add_generation_prompt (`bool`, *optional*, defaults to `False`):341                Whether to add a generation prompt at the end for the assistant to continue.342            tokenize (`bool`, *optional*, defaults to `True`):343                Whether to tokenize the output. If `False`, returns a string.344            padding (`bool`, *optional*, defaults to `False`):345                Whether to pad sequences.346            truncation (`bool`, *optional*, defaults to `False`):347                Whether to truncate sequences.348            max_length (`int`, *optional*):349                Maximum length of the output.350            return_tensors (`str`, *optional*):351                The type of tensors to return ("pt", "tf", "np", or None).352            return_dict (`bool`, *optional*, defaults to `False`):353                Whether to return a dictionary with additional information.354            **tokenizer_kwargs:355                Additional keyword arguments passed to the tokenizer.356                357        Returns:358            `Union[str, List[int], BatchEncoding]`: The formatted (and optionally tokenized) conversation.359            360        Example:361            ```python362            >>> tokenizer = IQuestCoderTokenizer.from_pretrained("path/to/model")363            >>> conversation = [364            ...     {"role": "system", "content": "You are a helpful assistant."},365            ...     {"role": "user", "content": "Hello!"},366            ...     {"role": "assistant", "content": "Hi there! How can I help you today?"},367            ...     {"role": "user", "content": "What's the weather like?"},368            ... ]369            >>> tokenizer.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False)370            '<|system|>\\nYou are a helpful assistant.\\n</|system|><|user|>\\nHello!\\n</|user|>...'371            ```372        """373        # Use parent class implementation with our template374        return super().apply_chat_template(375            conversation,376            chat_template=chat_template,377            add_generation_prompt=add_generation_prompt,378            tokenize=tokenize,379            padding=padding,380            truncation=truncation,381            max_length=max_length,382            return_tensors=return_tensors,383            return_dict=return_dict,384            **tokenizer_kwargs,385        )386 387 388# Try to import and create Fast tokenizer version389try:390    from transformers import PreTrainedTokenizerFast391    from tokenizers import Tokenizer, decoders, models, normalizers, pre_tokenizers, processors392    393    class IQuestCoderTokenizerFast(PreTrainedTokenizerFast):394        """395        Construct a "fast" IQuestCoder tokenizer (backed by HuggingFace's *tokenizers* library).396        397        This is a fast implementation of [`IQuestCoderTokenizer`] using the ๐Ÿค— Tokenizers library.398        399        Args:400            vocab_file (`str`, *optional*):401                Path to the vocabulary file (SentencePiece model).402            tokenizer_file (`str`, *optional*):403                Path to a tokenizer JSON file.404            unk_token (`str`, *optional*, defaults to `"<unk>"`):405                The unknown token.406            bos_token (`str`, *optional*, defaults to `"<s>"`):407                The beginning of sequence token.408            eos_token (`str`, *optional*, defaults to `"</s>"`):409                The end of sequence token.410            pad_token (`str`, *optional*):411                The token used for padding.412            add_bos_token (`bool`, *optional*, defaults to `True`):413                Whether to add a BOS token at the start of sequences.414            add_eos_token (`bool`, *optional*, defaults to `False`):415                Whether to add an EOS token at the end of sequences.416            add_prefix_space (`bool`, *optional*, defaults to `False`):417                Whether to add an initial space to the input.418            use_default_system_prompt (`bool`, *optional*, defaults to `False`):419                Whether to use the default system prompt.420            chat_template (`str`, *optional*):421                A Jinja template for formatting conversations.422                423        Example:424            ```python425            >>> from tokenization_iquestcoder import IQuestCoderTokenizerFast426            427            >>> tokenizer = IQuestCoderTokenizerFast.from_pretrained("path/to/model")428            >>> tokenizer.encode("Hello, world!")429            [1, 15043, 29892, 3186, 29991]430            ```431        """432        433        vocab_files_names = VOCAB_FILES_NAMES434        pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP435        max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES436        model_input_names = ["input_ids", "attention_mask"]437        slow_tokenizer_class = IQuestCoderTokenizer438        439        def __init__(440            self,441            vocab_file=None,442            tokenizer_file=None,443            unk_token="<unk>",444            bos_token="<s>",445            eos_token="</s>",446            pad_token=None,447            add_bos_token=True,448            add_eos_token=False,449            add_prefix_space=False,450            use_default_system_prompt=False,451            chat_template=None,452            **kwargs,453        ):454            self.add_bos_token = add_bos_token455            self.add_eos_token = add_eos_token456            self.add_prefix_space = add_prefix_space457            self.use_default_system_prompt = use_default_system_prompt458            self.vocab_file = vocab_file459 460            if chat_template is None:461                chat_template = DEFAULT_CHAT_TEMPLATE462            463            super().__init__(464                vocab_file=vocab_file,465                tokenizer_file=tokenizer_file,466                unk_token=unk_token,467                bos_token=bos_token,468                eos_token=eos_token,469                pad_token=pad_token,470                add_bos_token=add_bos_token,471                add_eos_token=add_eos_token,472                add_prefix_space=add_prefix_space,473                use_default_system_prompt=use_default_system_prompt,474                chat_template=chat_template,475                **kwargs,476            )477        478        @property479        def can_save_slow_tokenizer(self) -> bool:480            vocab_file = getattr(self, "vocab_file", None)481            return bool(vocab_file) and os.path.isfile(vocab_file)482 483        def save_vocabulary(484            self, save_directory: str, filename_prefix: Optional[str] = None485        ) -> Tuple[str]:486            if not self.can_save_slow_tokenizer:487                return ()488            if not os.path.isdir(save_directory):489                logger.error(f"Vocabulary path ({save_directory}) should be a directory")490                return ()491            out_vocab_file = os.path.join(492                save_directory,493                (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"],494            )495            if os.path.abspath(self.vocab_file) != os.path.abspath(out_vocab_file):496                copyfile(self.vocab_file, out_vocab_file)497            return (out_vocab_file,)498 499        @property500        def default_chat_template(self) -> str:501            """Returns the default chat template."""502            return DEFAULT_CHAT_TEMPLATE503        504        def build_inputs_with_special_tokens(505            self, 506            token_ids_0: List[int], 507            token_ids_1: Optional[List[int]] = None508        ) -> List[int]:509            """Build model inputs with special tokens."""510            bos_token_id = [self.bos_token_id] if self.add_bos_token else []511            eos_token_id = [self.eos_token_id] if self.add_eos_token else []512 513            output = bos_token_id + token_ids_0 + eos_token_id514 515            if token_ids_1 is not None:516                output = output + bos_token_id + token_ids_1 + eos_token_id517 518            return output519        520        def get_special_tokens_mask(521            self, 522            token_ids_0: List[int], 523            token_ids_1: Optional[List[int]] = None, 524            already_has_special_tokens: bool = False525        ) -> List[int]:526            """Retrieve special tokens mask."""527            if already_has_special_tokens:528                return super().get_special_tokens_mask(529                    token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True530                )531 532            bos_token_id = [1] if self.add_bos_token else []533            eos_token_id = [1] if self.add_eos_token else []534 535            if token_ids_1 is None:536                return bos_token_id + ([0] * len(token_ids_0)) + eos_token_id537            return (538                bos_token_id539                + ([0] * len(token_ids_0))540                + eos_token_id541                + bos_token_id542                + ([0] * len(token_ids_1))543                + eos_token_id544            )545        546        def create_token_type_ids_from_sequences(547            self, 548            token_ids_0: List[int], 549            token_ids_1: Optional[List[int]] = None550        ) -> List[int]:551            """Create token type IDs from sequences."""552            bos_token_id = [self.bos_token_id] if self.add_bos_token else []553            eos_token_id = [self.eos_token_id] if self.add_eos_token else []554 555            output = [0] * len(bos_token_id + token_ids_0 + eos_token_id)556 557            if token_ids_1 is not None:558                output += [1] * len(bos_token_id + token_ids_1 + eos_token_id)559 560            return output561 562except ImportError:563    # tokenizers library not available, Fast tokenizer not supported564    IQuestCoderTokenizerFast = None565    logger.info(566        "The `tokenizers` library is not installed. "567        "IQuestCoderTokenizerFast will not be available. "568        "Install it with `pip install tokenizers`."569    )570 571