IntMeGroup/ICCVW_st2_mos2
03
1import os2from shutil import copyfile3from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple4 5import sentencepiece as spm6from transformers.tokenization_utils import AddedToken, PreTrainedTokenizer7from transformers.utils import logging8 9if TYPE_CHECKING:10 from transformers.tokenization_utils_base import TextInput11 12logger = logging.get_logger(__name__)13 14VOCAB_FILES_NAMES = {"vocab_file": "tokenizer.model"}15 16SPIECE_UNDERLINE = "โ"17 18 19class InternLM3Tokenizer(PreTrainedTokenizer):20 """21 Construct a InternLM3 tokenizer. Based on byte-level Byte-Pair-Encoding. The default padding token is unset as there is22 no padding token in the original model.23 24 Args:25 vocab_file (`str`):26 Path to the vocabulary file.27 unk_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<unk>"`):28 The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this29 token instead.30 bos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<s>"`):31 The beginning of sequence token that was used during pretraining. Can be used a sequence classifier token.32 eos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"</s>"`):33 The end of sequence token.34 pad_token (`str` or `tokenizers.AddedToken`, *optional*):35 A special token used to make arrays of tokens the same size for batching purpose. Will then be ignored by36 attention mechanisms or loss computation.37 sp_model_kwargs (`Dict[str, Any]`, `Optional`, *optional*):38 Will be passed to the `SentencePieceProcessor.__init__()` method. The [Python wrapper for39 SentencePiece](https://github.com/google/sentencepiece/tree/master/python) can be used, among other things,40 to set:41 42 - `enable_sampling`: Enable subword regularization.43 - `nbest_size`: Sampling parameters for unigram. Invalid for BPE-Dropout.44 45 - `nbest_size = {0,1}`: No sampling is performed.46 - `nbest_size > 1`: samples from the nbest_size results.47 - `nbest_size < 0`: assuming that nbest_size is infinite and samples from the all hypothesis (lattice)48 using forward-filtering-and-backward-sampling algorithm.49 50 - `alpha`: Smoothing parameter for unigram sampling, and dropout probability of merge operations for51 BPE-dropout.52 53 add_bos_token (`bool`, *optional*, defaults to `True`):54 Whether or not to add an `bos_token` at the start of sequences.55 add_eos_token (`bool`, *optional*, defaults to `False`):56 Whether or not to add an `eos_token` at the end of sequences.57 clean_up_tokenization_spaces (`bool`, *optional*, defaults to `False`):58 Whether or not to cleanup spaces after decoding, cleanup consists in removing potential artifacts like59 extra spaces.60 use_default_system_prompt (`bool`, *optional*, defaults to `False`):61 Whether or not the default system prompt for InternLM3 should be used.62 spaces_between_special_tokens (`bool`, *optional*, defaults to `False`):63 Whether or not to add spaces between special tokens.64 spaces_for_interleaved_special_tokens (`bool`, *optional*, defaults to `False`):65 Whether or not to add spaces between special tokens that are interleaved with normal tokens.66 add_prefix_space (`bool`, *optional*, defaults to `True`):67 Whether or not to add an initial space to the input. This allows to treat the leading word just as any68 other word. Again, this should be set with `from_slow=True` to make sure it's taken into account.69 """70 71 vocab_files_names = VOCAB_FILES_NAMES72 model_input_names = ["input_ids", "attention_mask"]73 74 def __init__(75 self,76 vocab_file,77 unk_token="<unk>",78 bos_token="<s>",79 eos_token="</s>",80 pad_token=None,81 sp_model_kwargs: Optional[Dict[str, Any]] = None,82 add_bos_token=True,83 add_eos_token=False,84 clean_up_tokenization_spaces=False,85 use_default_system_prompt=False,86 spaces_between_special_tokens=False,87 spaces_for_interleaved_special_tokens=False,88 add_prefix_space=True,89 **kwargs,90 ):91 self.sp_model_kwargs = {} if sp_model_kwargs is None else sp_model_kwargs92 bos_token = AddedToken(bos_token, normalized=False, special=True) if isinstance(bos_token, str) else bos_token93 eos_token = AddedToken(eos_token, normalized=False, special=True) if isinstance(eos_token, str) else eos_token94 unk_token = AddedToken(unk_token, normalized=False, special=True) if isinstance(unk_token, str) else unk_token95 pad_token = AddedToken(pad_token, normalized=False, special=True) if isinstance(pad_token, str) else pad_token96 97 self.vocab_file = vocab_file98 self.add_bos_token = add_bos_token99 self.add_eos_token = add_eos_token100 self.use_default_system_prompt = use_default_system_prompt101 self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)102 self.sp_model.Load(vocab_file)103 self.add_prefix_space = add_prefix_space104 self.spaces_for_interleaved_special_tokens = spaces_for_interleaved_special_tokens105 106 vocab_size = self.sp_model.get_piece_size()107 self.decoder = {i: self.sp_model.id_to_piece(i) for i in range(vocab_size)}108 109 super().__init__(110 bos_token=bos_token,111 eos_token=eos_token,112 unk_token=unk_token,113 pad_token=pad_token,114 add_bos_token=add_bos_token,115 add_eos_token=add_eos_token,116 sp_model_kwargs=sp_model_kwargs,117 clean_up_tokenization_spaces=clean_up_tokenization_spaces,118 use_default_system_prompt=use_default_system_prompt,119 spaces_between_special_tokens=spaces_between_special_tokens,120 add_prefix_space=add_prefix_space,121 **kwargs,122 )123 124 def __getstate__(self):125 state = self.__dict__.copy()126 state["sp_model"] = None127 state["sp_model_proto"] = self.sp_model.serialized_model_proto()128 return state129 130 def __setstate__(self, d):131 self.__dict__.update(d)132 self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)133 self.sp_model.LoadFromSerializedProto(self.sp_model_proto)134 135 @property136 def vocab_size(self):137 """Returns vocab size"""138 return self.sp_model.get_piece_size()139 140 def get_vocab(self):141 """Returns vocab as a dict"""142 vocab = {self.convert_ids_to_tokens(i): i for i in range(self.vocab_size)}143 vocab.update(self.added_tokens_encoder)144 return vocab145 146 def tokenize(self, text: "TextInput", **kwargs) -> List[str]:147 """148 Args:149 text: TextInput150 Simply calls PreTrainedTokenizer's method151 """152 return super().tokenize(text, **kwargs)153 154 def _tokenize(self, text, **kwargs):155 """156 Args:157 text: TextInput158 Returns a tokenized string. The Gemma tokenizer never adds a prefix space.159 """160 return self.sp_model.encode(text, out_type=str)161 162 def _convert_token_to_id(self, token):163 """Converts a token (str) in an id using the vocab."""164 return self.sp_model.piece_to_id(token)165 166 def _convert_id_to_token(self, index):167 """Converts an index (integer) in a token (str) using the vocab."""168 return self.decoder.get(index, "")169 170 def convert_tokens_to_string(self, tokens):171 """Converts a sequence of tokens (string) in a single string."""172 # since we manually add the prefix space, we have to remove it when decoding173 if tokens[0].startswith(SPIECE_UNDERLINE) and self.add_prefix_space:174 tokens[0] = tokens[0][1:]175 176 current_sub_tokens = []177 out_string = ""178 prev_is_special = False179 for i, token in enumerate(tokens):180 # make sure that special tokens are not decoded using sentencepiece model181 if token in self.all_special_tokens:182 if not prev_is_special and i != 0 and self.spaces_for_interleaved_special_tokens:183 out_string += " "184 out_string += self.sp_model.decode(current_sub_tokens) + token185 prev_is_special = True186 current_sub_tokens = []187 else:188 if (189 prev_is_special190 and i == 1191 and self.add_prefix_space192 and not token.startswith(SPIECE_UNDERLINE)193 and self.spaces_for_interleaved_special_tokens194 ):195 out_string += " "196 current_sub_tokens.append(token)197 prev_is_special = False198 out_string += self.sp_model.decode(current_sub_tokens)199 return out_string200 201 def save_vocabulary(self, save_directory, filename_prefix: Optional[str] = None) -> Tuple[str]:202 """203 Save the vocabulary and special tokens file to a directory.204 205 Args:206 save_directory (`str`):207 The directory in which to save the vocabulary.208 209 Returns:210 `Tuple(str)`: Paths to the files saved.211 """212 if not os.path.isdir(save_directory):213 logger.error(f"Vocabulary path ({save_directory}) should be a directory")214 return215 out_vocab_file = os.path.join(save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"])216 217 if os.path.abspath(self.vocab_file) != os.path.abspath(out_vocab_file) and os.path.isfile(self.vocab_file):218 copyfile(self.vocab_file, out_vocab_file)219 elif not os.path.isfile(self.vocab_file):220 with open(out_vocab_file, "wb") as fi:221 content_spiece_model = self.sp_model.serialized_model_proto()222 fi.write(content_spiece_model)223 224 return (out_vocab_file,)225 226 def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None):227 bos_token_id = [self.bos_token_id] if self.add_bos_token else []228 eos_token_id = [self.eos_token_id] if self.add_eos_token else []229 230 output = bos_token_id + token_ids_0 + eos_token_id231 232 if token_ids_1 is not None:233 output = output + bos_token_id + token_ids_1 + eos_token_id234 235 return output236 237 def get_special_tokens_mask(238 self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None, 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(token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True)257 258 bos_token_id = [1] if self.add_bos_token else []259 eos_token_id = [1] if self.add_eos_token else []260 261 if token_ids_1 is None:262 return bos_token_id + ([0] * len(token_ids_0)) + eos_token_id263 return bos_token_id + ([0] * len(token_ids_0)) + eos_token_id + bos_token_id + ([0] * len(token_ids_1)) + eos_token_id264 265 def create_token_type_ids_from_sequences(self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None) -> List[int]:266 """267 Creates a mask from the two sequences passed to be used in a sequence-pair classification task. An ALBERT268 sequence pair mask has the following format:269 270 ```271 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1272 | first sequence | second sequence |273 ```274 275 if token_ids_1 is None, only returns the first portion of the mask (0s).276 277 Args:278 token_ids_0 (`List[int]`):279 List of ids.280 token_ids_1 (`List[int]`, *optional*):281 Optional second list of IDs for sequence pairs.282 283 Returns:284 `List[int]`: List of [token type IDs](../glossary#token-type-ids) according to the given sequence(s).285 """286 bos_token_id = [self.bos_token_id] if self.add_bos_token else []287 eos_token_id = [self.eos_token_id] if self.add_eos_token else []288 289 output = [0] * len(bos_token_id + token_ids_0 + eos_token_id)290 291 if token_ids_1 is not None:292 output += [1] * len(bos_token_id + token_ids_1 + eos_token_id)293 294 return output295 