SunderAli17 commited on
Commit
b6f3ee6
1 Parent(s): 0e4df08

Create tokenizer/tokenization_chatglm.py

Browse files
Files changed (1) hide show
  1. tokenizer/tokenization_chatglm.py +292 -0
tokenizer/tokenization_chatglm.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import re
4
+ from typing import List, Optional, Union, Dict
5
+ from sentencepiece import SentencePieceProcessor
6
+ from transformers import PreTrainedTokenizer
7
+ from transformers.utils import logging, PaddingStrategy
8
+ from transformers.tokenization_utils_base import EncodedInput, BatchEncoding
9
+
10
+
11
+ class SPTokenizer:
12
+ def __init__(self, model_path: str):
13
+ # reload tokenizer
14
+ assert os.path.isfile(model_path), model_path
15
+ self.sp_model = SentencePieceProcessor(model_file=model_path)
16
+
17
+ # BOS / EOS token IDs
18
+ self.n_words: int = self.sp_model.vocab_size()
19
+ self.bos_id: int = self.sp_model.bos_id()
20
+ self.eos_id: int = self.sp_model.eos_id()
21
+ self.pad_id: int = self.sp_model.unk_id()
22
+ assert self.sp_model.vocab_size() == self.sp_model.get_piece_size()
23
+
24
+ role_special_tokens = ["<|system|>", "<|user|>", "<|assistant|>", "<|observation|>"]
25
+ special_tokens = ["[MASK]", "[gMASK]", "[sMASK]", "sop", "eop"] + role_special_tokens
26
+ self.special_tokens = {}
27
+ self.index_special_tokens = {}
28
+ for token in special_tokens:
29
+ self.special_tokens[token] = self.n_words
30
+ self.index_special_tokens[self.n_words] = token
31
+ self.n_words += 1
32
+ self.role_special_token_expression = "|".join([re.escape(token) for token in role_special_tokens])
33
+
34
+ def tokenize(self, s: str, encode_special_tokens=False):
35
+ if encode_special_tokens:
36
+ last_index = 0
37
+ t = []
38
+ for match in re.finditer(self.role_special_token_expression, s):
39
+ if last_index < match.start():
40
+ t.extend(self.sp_model.EncodeAsPieces(s[last_index:match.start()]))
41
+ t.append(s[match.start():match.end()])
42
+ last_index = match.end()
43
+ if last_index < len(s):
44
+ t.extend(self.sp_model.EncodeAsPieces(s[last_index:]))
45
+ return t
46
+ else:
47
+ return self.sp_model.EncodeAsPieces(s)
48
+
49
+ def encode(self, s: str, bos: bool = False, eos: bool = False) -> List[int]:
50
+ assert type(s) is str
51
+ t = self.sp_model.encode(s)
52
+ if bos:
53
+ t = [self.bos_id] + t
54
+ if eos:
55
+ t = t + [self.eos_id]
56
+ return t
57
+
58
+ def decode(self, t: List[int]) -> str:
59
+ text, buffer = "", []
60
+ for token in t:
61
+ if token in self.index_special_tokens:
62
+ if buffer:
63
+ text += self.sp_model.decode(buffer)
64
+ buffer = []
65
+ text += self.index_special_tokens[token]
66
+ else:
67
+ buffer.append(token)
68
+ if buffer:
69
+ text += self.sp_model.decode(buffer)
70
+ return text
71
+
72
+ def decode_tokens(self, tokens: List[str]) -> str:
73
+ text = self.sp_model.DecodePieces(tokens)
74
+ return text
75
+
76
+ def convert_token_to_id(self, token):
77
+ """ Converts a token (str) in an id using the vocab. """
78
+ if token in self.special_tokens:
79
+ return self.special_tokens[token]
80
+ return self.sp_model.PieceToId(token)
81
+
82
+ def convert_id_to_token(self, index):
83
+ """Converts an index (integer) in a token (str) using the vocab."""
84
+ if index in self.index_special_tokens:
85
+ return self.index_special_tokens[index]
86
+ if index in [self.eos_id, self.bos_id, self.pad_id] or index < 0:
87
+ return ""
88
+ return self.sp_model.IdToPiece(index)
89
+
90
+
91
+ class ChatGLMTokenizer(PreTrainedTokenizer):
92
+ vocab_files_names = {"vocab_file": "tokenizer.model"}
93
+
94
+ model_input_names = ["input_ids", "attention_mask", "position_ids"]
95
+
96
+ def __init__(self, vocab_file, padding_side="left", clean_up_tokenization_spaces=False, encode_special_tokens=False,
97
+ **kwargs):
98
+ self.name = "GLMTokenizer"
99
+
100
+ self.vocab_file = vocab_file
101
+ self.tokenizer = SPTokenizer(vocab_file)
102
+ self.special_tokens = {
103
+ "<bos>": self.tokenizer.bos_id,
104
+ "<eos>": self.tokenizer.eos_id,
105
+ "<pad>": self.tokenizer.pad_id
106
+ }
107
+ self.encode_special_tokens = encode_special_tokens
108
+ super().__init__(padding_side=padding_side, clean_up_tokenization_spaces=clean_up_tokenization_spaces,
109
+ encode_special_tokens=encode_special_tokens,
110
+ **kwargs)
111
+
112
+ def get_command(self, token):
113
+ if token in self.special_tokens:
114
+ return self.special_tokens[token]
115
+ assert token in self.tokenizer.special_tokens, f"{token} is not a special token for {self.name}"
116
+ return self.tokenizer.special_tokens[token]
117
+
118
+ @property
119
+ def unk_token(self) -> str:
120
+ return "<unk>"
121
+
122
+ @property
123
+ def pad_token(self) -> str:
124
+ return "<unk>"
125
+
126
+ @property
127
+ def pad_token_id(self):
128
+ return self.get_command("<pad>")
129
+
130
+ @property
131
+ def eos_token(self) -> str:
132
+ return "</s>"
133
+
134
+ @property
135
+ def eos_token_id(self):
136
+ return self.get_command("<eos>")
137
+
138
+ @property
139
+ def vocab_size(self):
140
+ return self.tokenizer.n_words
141
+
142
+ def get_vocab(self):
143
+ """ Returns vocab as a dict """
144
+ vocab = {self._convert_id_to_token(i): i for i in range(self.vocab_size)}
145
+ vocab.update(self.added_tokens_encoder)
146
+ return vocab
147
+
148
+ def _tokenize(self, text, **kwargs):
149
+ return self.tokenizer.tokenize(text, encode_special_tokens=self.encode_special_tokens)
150
+
151
+ def _convert_token_to_id(self, token):
152
+ """ Converts a token (str) in an id using the vocab. """
153
+ return self.tokenizer.convert_token_to_id(token)
154
+
155
+ def _convert_id_to_token(self, index):
156
+ """Converts an index (integer) in a token (str) using the vocab."""
157
+ return self.tokenizer.convert_id_to_token(index)
158
+
159
+ def convert_tokens_to_string(self, tokens: List[str]) -> str:
160
+ return self.tokenizer.decode_tokens(tokens)
161
+
162
+ def save_vocabulary(self, save_directory, filename_prefix=None):
163
+ """
164
+ Save the vocabulary and special tokens file to a directory.
165
+ Args:
166
+ save_directory (`str`):
167
+ The directory in which to save the vocabulary.
168
+ filename_prefix (`str`, *optional*):
169
+ An optional prefix to add to the named of the saved files.
170
+ Returns:
171
+ `Tuple(str)`: Paths to the files saved.
172
+ """
173
+ if os.path.isdir(save_directory):
174
+ vocab_file = os.path.join(
175
+ save_directory, self.vocab_files_names["vocab_file"]
176
+ )
177
+ else:
178
+ vocab_file = save_directory
179
+
180
+ with open(self.vocab_file, 'rb') as fin:
181
+ proto_str = fin.read()
182
+
183
+ with open(vocab_file, "wb") as writer:
184
+ writer.write(proto_str)
185
+
186
+ return (vocab_file,)
187
+
188
+ def get_prefix_tokens(self):
189
+ prefix_tokens = [self.get_command("[gMASK]"), self.get_command("sop")]
190
+ return prefix_tokens
191
+
192
+ def build_single_message(self, role, metadata, message):
193
+ assert role in ["system", "user", "assistant", "observation"], role
194
+ role_tokens = [self.get_command(f"<|{role}|>")] + self.tokenizer.encode(f"{metadata}\n")
195
+ message_tokens = self.tokenizer.encode(message)
196
+ tokens = role_tokens + message_tokens
197
+ return tokens
198
+
199
+ def build_chat_input(self, query, history=None, role="user"):
200
+ if history is None:
201
+ history = []
202
+ input_ids = []
203
+ for item in history:
204
+ content = item["content"]
205
+ if item["role"] == "system" and "tools" in item:
206
+ content = content + "\n" + json.dumps(item["tools"], indent=4, ensure_ascii=False)
207
+ input_ids.extend(self.build_single_message(item["role"], item.get("metadata", ""), content))
208
+ input_ids.extend(self.build_single_message(role, "", query))
209
+ input_ids.extend([self.get_command("<|assistant|>")])
210
+ return self.batch_encode_plus([input_ids], return_tensors="pt", is_split_into_words=True)
211
+
212
+ def build_inputs_with_special_tokens(
213
+ self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None
214
+ ) -> List[int]:
215
+ """
216
+ Build model inputs from a sequence or a pair of sequence for sequence classification tasks by concatenating and
217
+ adding special tokens. A BERT sequence has the following format:
218
+ - single sequence: `[CLS] X [SEP]`
219
+ - pair of sequences: `[CLS] A [SEP] B [SEP]`
220
+ Args:
221
+ token_ids_0 (`List[int]`):
222
+ List of IDs to which the special tokens will be added.
223
+ token_ids_1 (`List[int]`, *optional*):
224
+ Optional second list of IDs for sequence pairs.
225
+ Returns:
226
+ `List[int]`: List of [input IDs](../glossary#input-ids) with the appropriate special tokens.
227
+ """
228
+ prefix_tokens = self.get_prefix_tokens()
229
+ token_ids_0 = prefix_tokens + token_ids_0
230
+ if token_ids_1 is not None:
231
+ token_ids_0 = token_ids_0 + token_ids_1 + [self.get_command("<eos>")]
232
+ return token_ids_0
233
+
234
+ def _pad(
235
+ self,
236
+ encoded_inputs: Union[Dict[str, EncodedInput], BatchEncoding],
237
+ max_length: Optional[int] = None,
238
+ padding_strategy: PaddingStrategy = PaddingStrategy.DO_NOT_PAD,
239
+ pad_to_multiple_of: Optional[int] = None,
240
+ return_attention_mask: Optional[bool] = None,
241
+ ) -> dict:
242
+ """
243
+ Pad encoded inputs (on left/right and up to predefined length or max length in the batch)
244
+ Args:
245
+ encoded_inputs:
246
+ Dictionary of tokenized inputs (`List[int]`) or batch of tokenized inputs (`List[List[int]]`).
247
+ max_length: maximum length of the returned list and optionally padding length (see below).
248
+ Will truncate by taking into account the special tokens.
249
+ padding_strategy: PaddingStrategy to use for padding.
250
+ - PaddingStrategy.LONGEST Pad to the longest sequence in the batch
251
+ - PaddingStrategy.MAX_LENGTH: Pad to the max length (default)
252
+ - PaddingStrategy.DO_NOT_PAD: Do not pad
253
+ The tokenizer padding sides are defined in self.padding_side:
254
+ - 'left': pads on the left of the sequences
255
+ - 'right': pads on the right of the sequences
256
+ pad_to_multiple_of: (optional) Integer if set will pad the sequence to a multiple of the provided value.
257
+ This is especially useful to enable the use of Tensor Core on NVIDIA hardware with compute capability
258
+ `>= 7.5` (Volta).
259
+ return_attention_mask:
260
+ (optional) Set to False to avoid returning attention mask (default: set to model specifics)
261
+ """
262
+ # Load from model defaults
263
+ assert self.padding_side == "left"
264
+
265
+ required_input = encoded_inputs[self.model_input_names[0]]
266
+ seq_length = len(required_input)
267
+
268
+ if padding_strategy == PaddingStrategy.LONGEST:
269
+ max_length = len(required_input)
270
+
271
+ if max_length is not None and pad_to_multiple_of is not None and (max_length % pad_to_multiple_of != 0):
272
+ max_length = ((max_length // pad_to_multiple_of) + 1) * pad_to_multiple_of
273
+
274
+ needs_to_be_padded = padding_strategy != PaddingStrategy.DO_NOT_PAD and len(required_input) != max_length
275
+
276
+ # Initialize attention mask if not present.
277
+ if "attention_mask" not in encoded_inputs:
278
+ encoded_inputs["attention_mask"] = [1] * seq_length
279
+
280
+ if "position_ids" not in encoded_inputs:
281
+ encoded_inputs["position_ids"] = list(range(seq_length))
282
+
283
+ if needs_to_be_padded:
284
+ difference = max_length - len(required_input)
285
+
286
+ if "attention_mask" in encoded_inputs:
287
+ encoded_inputs["attention_mask"] = [0] * difference + encoded_inputs["attention_mask"]
288
+ if "position_ids" in encoded_inputs:
289
+ encoded_inputs["position_ids"] = [0] * difference + encoded_inputs["position_ids"]
290
+ encoded_inputs[self.model_input_names[0]] = [self.pad_token_id] * difference + required_input
291
+
292
+ return encoded_inputs